Saya akan menggunakan TPL Dataflow untuk ini (karena Anda menggunakan .NET 4.5 dan digunakan secara Taskinternal). 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 DateTimeOffsetstruktur ; 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 Postmetode ekstensi itu sendiri).
Saya juga telah melewati CancellationTokenstruktur ke konstruktor ActionBlock<TInput>dan ke pemanggilan Task.Delaymetode ; 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 Postmetode ekstensi):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
StartWorkMetode Anda :
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
task.Post(DateTimeOffset.Now);
}
Dan kemudian StopWorkmetode Anda :
void StopWork()
{
using (wtoken)
{
wtoken.Cancel();
}
wtoken = null;
task = null;
}
Mengapa Anda ingin menggunakan TPL Dataflow di sini? Beberapa alasan:
Pemisahan kekhawatiran
The CreateNeverEndingTaskMetode 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 Taskyang akan memproses kelanjutan, tetapi itu harus kecil, mengingat Anda tidak memproses ini dalam loop ketat (Anda menunggu sepuluh detik di antara pemanggilan).
Jika DoWorkfungsi 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 CancellationTokenmetode Anda (jika menerimanya), yang dilakukan di sini.
Itu berarti Anda kemudian akan memiliki DoWorkAsyncmetode dengan tanda tangan berikut:
Task DoWorkAsync(CancellationToken cancellationToken);
Anda harus mengubah (hanya sedikit, dan Anda tidak mengeluarkan pemisahan masalah di sini) StartWorkmetode untuk memperhitungkan tanda tangan baru yang diteruskan ke CreateNeverEndingTaskmetode, seperti:
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
task.Post(DateTimeOffset.Now, wtoken.Token);
}