Saya akan menggunakan TPL Dataflow untuk ini (karena Anda menggunakan .NET 4.5 dan digunakan secara Task
internal). Anda dapat dengan mudah membuat ActionBlock<TInput>
item yang memposting ke dirinya sendiri setelah diproses tindakannya dan menunggu waktu yang sesuai.
Pertama, buat pabrik yang akan membuat tugas tanpa akhir Anda:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Action<DateTimeOffset> action, CancellationToken cancellationToken)
{
if (action == null) throw new ArgumentNullException("action");
ActionBlock<DateTimeOffset> block = null;
block = new ActionBlock<DateTimeOffset>(async now => {
action(now);
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
ConfigureAwait(false);
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
return block;
}
Saya telah memilih ActionBlock<TInput>
untuk mengambil DateTimeOffset
struktur ; Anda harus meneruskan parameter type, dan mungkin juga meneruskan beberapa status yang berguna (Anda dapat mengubah sifat status, jika Anda mau).
Selain itu, perhatikan bahwa ActionBlock<TInput>
secara default hanya memproses satu item dalam satu waktu, jadi Anda dijamin bahwa hanya satu tindakan yang akan diproses (artinya, Anda tidak perlu berurusan dengan reentrancy saat memanggil kembali Post
metode ekstensi itu sendiri).
Saya juga telah melewati CancellationToken
struktur ke konstruktor ActionBlock<TInput>
dan ke pemanggilan Task.Delay
metode ; jika proses dibatalkan, pembatalan akan dilakukan pada kesempatan pertama yang memungkinkan.
Dari sana, pemfaktoran ulang kode Anda mudah dilakukan untuk menyimpan ITargetBlock<DateTimeoffset>
antarmuka yang diterapkan oleh ActionBlock<TInput>
(ini adalah abstraksi tingkat yang lebih tinggi yang mewakili blok yang merupakan konsumen, dan Anda ingin dapat memicu konsumsi melalui panggilan ke Post
metode ekstensi):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
StartWork
Metode Anda :
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
task.Post(DateTimeOffset.Now);
}
Dan kemudian StopWork
metode Anda :
void StopWork()
{
using (wtoken)
{
wtoken.Cancel();
}
wtoken = null;
task = null;
}
Mengapa Anda ingin menggunakan TPL Dataflow di sini? Beberapa alasan:
Pemisahan kekhawatiran
The CreateNeverEndingTask
Metode sekarang menjadi pabrik yang menciptakan "layanan" Anda sehingga untuk berbicara. Anda mengontrol kapan itu mulai dan berhenti, dan itu benar-benar mandiri. Anda tidak perlu menjalin kontrol status pengatur waktu dengan aspek lain dari kode Anda. Anda cukup membuat blok, memulainya, dan menghentikannya setelah selesai.
Penggunaan utas / tugas / sumber daya yang lebih efisien
Penjadwal default untuk blok dalam aliran data TPL adalah sama untuk a Task
, yang merupakan kumpulan utas. Dengan menggunakan ActionBlock<TInput>
to memproses tindakan Anda, serta panggilan ke Task.Delay
, Anda menghasilkan kendali atas utas yang Anda gunakan saat Anda tidak benar-benar melakukan apa pun. Memang, ini sebenarnya mengarah ke beberapa overhead saat Anda menelurkan yang baru Task
yang akan memproses kelanjutan, tetapi itu harus kecil, mengingat Anda tidak memproses ini dalam loop ketat (Anda menunggu sepuluh detik di antara pemanggilan).
Jika DoWork
fungsi benar-benar dapat dibuat menjadi menunggu (yaitu, dalam mengembalikan a Task
), maka Anda (mungkin) dapat mengoptimalkan ini lebih banyak lagi dengan mengubah metode pabrik di atas untuk mengambil Func<DateTimeOffset, CancellationToken, Task>
alih - alih Action<DateTimeOffset>
, seperti:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
if (action == null) throw new ArgumentNullException("action");
ActionBlock<DateTimeOffset> block = null;
block = new ActionBlock<DateTimeOffset>(async now => {
await action(now, cancellationToken).
ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
ConfigureAwait(false);
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
return block;
}
Tentu saja, akan menjadi praktik yang baik untuk merangkai CancellationToken
metode Anda (jika menerimanya), yang dilakukan di sini.
Itu berarti Anda kemudian akan memiliki DoWorkAsync
metode dengan tanda tangan berikut:
Task DoWorkAsync(CancellationToken cancellationToken);
Anda harus mengubah (hanya sedikit, dan Anda tidak mengeluarkan pemisahan masalah di sini) StartWork
metode untuk memperhitungkan tanda tangan baru yang diteruskan ke CreateNeverEndingTask
metode, seperti:
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
task.Post(DateTimeOffset.Now, wtoken.Token);
}