Sampai sekarang saya menggunakan tugas LongRunning TPL untuk pekerjaan latar belakang terikat CPU siklik alih-alih pengatur waktu threading, karena:
- tugas TPL mendukung pembatalan
- pengatur waktu threading dapat memulai utas lain saat program dimatikan yang menyebabkan kemungkinan masalah dengan sumber daya yang dibuang
- peluang untuk dibanjiri: pengatur waktu threading dapat memulai utas lain sementara yang sebelumnya masih diproses karena pekerjaan panjang yang tidak terduga (saya tahu, ini dapat dicegah dengan menghentikan dan memulai ulang pengatur waktu)
Namun, solusi TPL selalu mengklaim utas khusus yang tidak diperlukan sambil menunggu tindakan berikutnya (yang paling sering). Saya ingin menggunakan solusi yang diusulkan Jeff untuk melakukan pekerjaan siklik terikat CPU di latar belakang karena hanya memerlukan utas threadpool ketika ada pekerjaan yang harus dilakukan yang lebih baik untuk skalabilitas (terutama bila periode intervalnya besar).
Untuk mencapai itu, saya menyarankan 4 adaptasi:
- Tambahkan
ConfigureAwait(false)
ke Task.Delay()
untuk mengeksekusi doWork
tindakan pada utas kumpulan utas, jika tidak doWork
akan dilakukan pada utas pemanggil yang bukan merupakan gagasan paralelisme
- Tetap berpegang pada pola pembatalan dengan melemparkan TaskCanceledException (masih diperlukan?)
- Teruskan CancellationToken ke
doWork
untuk mengaktifkannya membatalkan tugas
- Tambahkan parameter tipe objek untuk memberikan informasi status tugas (seperti tugas TPL)
Tentang poin 2 Saya tidak yakin, apakah async menunggu masih memerlukan TaskCanceledExecption atau itu hanya praktik terbaik?
public static async Task Run(Action<object, CancellationToken> doWork, object taskState, TimeSpan period, CancellationToken cancellationToken)
{
do
{
await Task.Delay(period, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
doWork(taskState, cancellationToken);
}
while (true);
}
Tolong berikan komentar Anda untuk solusi yang diusulkan ...
Perbarui 2016-8-30
Solusi di atas tidak segera memanggil doWork()
tetapi dimulai dengan await Task.Delay().ConfigureAwait(false)
untuk mencapai sakelar utas doWork()
. Solusi di bawah ini mengatasi masalah ini dengan membungkus doWork()
panggilan pertama dalam a Task.Run()
dan menunggunya.
Di bawah ini adalah pengganti async \ await yang ditingkatkan untuk Threading.Timer
menjalankan pekerjaan siklik yang dapat dibatalkan dan dapat diskalakan (dibandingkan dengan solusi TPL) karena tidak menempati utas apa pun saat menunggu tindakan berikutnya.
Perhatikan bahwa berlawanan dengan Timer, waktu tunggu ( period
) adalah konstan dan bukan waktu siklus; waktu siklus adalah jumlah waktu tunggu dan durasinya doWork()
dapat bervariasi.
public static async Task Run(Action<object, CancellationToken> doWork, object taskState, TimeSpan period, CancellationToken cancellationToken)
{
await Task.Run(() => doWork(taskState, cancellationToken), cancellationToken).ConfigureAwait(false);
do
{
await Task.Delay(period, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
doWork(taskState, cancellationToken);
}
while (true);
}