Bunun için TPL Dataflow'u kullanırım (çünkü .NET 4.5'i kullanıyorsunuz ve Task
dahili olarak kullanıyor ). ActionBlock<TInput>
İşlemi işlendikten ve uygun bir süre bekledikten sonra kendisine öğeleri gönderen bir öğeyi kolayca oluşturabilirsiniz .
Öncelikle, hiç bitmeyen görevinizi yaratacak bir fabrika oluşturun:
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;
}
ActionBlock<TInput>
Bir DateTimeOffset
yapı almak için seçtim ; bir tür parametresi iletmeniz gerekir ve bu, bazı yararlı durumları da iletebilir (isterseniz, durumun doğasını değiştirebilirsiniz).
Ayrıca, ActionBlock<TInput>
varsayılan olarak bir seferde yalnızca bir öğeyi işlediğini unutmayın , bu nedenle yalnızca bir eylemin işleneceği garanti edilir (yani, uzantı yöntemini kendi başına geri çağırdığında yeniden giriş yapmanız gerekmez ).Post
Ayrıca CancellationToken
yapıyı hem yapıcıya hem ActionBlock<TInput>
de Task.Delay
yöntem çağrısına geçirdim ; İşlem iptal edilirse, iptal mümkün olan ilk fırsatta gerçekleşecektir.
Oradan, uygulanan ITargetBlock<DateTimeoffset>
arabirimi depolamak için kodunuzun yeniden düzenlenmesi kolaydır ActionBlock<TInput>
(bu, tüketiciler olan blokları temsil eden üst düzey soyutlamadır ve Post
uzantı yöntemine yapılan bir çağrı yoluyla tüketimi tetikleyebilmek istersiniz ):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
Sizin StartWork
yöntemi:
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
task.Post(DateTimeOffset.Now);
}
Ve sonra StopWork
yönteminiz:
void StopWork()
{
using (wtoken)
{
wtoken.Cancel();
}
wtoken = null;
task = null;
}
TPL Dataflow'u neden burada kullanmak istersiniz? Birkaç neden:
Endişelerin ayrılması
CreateNeverEndingTask
Yöntem şimdi çok konuşmak için "hizmet" yaratan bir fabrikasıdır. Ne zaman başlayıp duracağını siz kontrol edersiniz ve tamamen bağımsızdır. Zamanlayıcının durum kontrolünü kodunuzun diğer yönleriyle iç içe geçirmeniz gerekmez. Basitçe bloğu yaratırsınız, başlatırsınız ve işiniz bittiğinde durdurursunuz.
İş parçacıkları / görevler / kaynakların daha verimli kullanımı
TPL veri akışındaki bloklar için varsayılan zamanlayıcı Task
, iş parçacığı havuzu olan a için aynıdır . Kullanarak ActionBlock<TInput>
Eyleminizi yanı sıra bir çağrı işlemek için Task.Delay
, size aslında hiçbir şey yapmıyorsun kullanmakta olduğu iplik kontrolünü elde ediyoruz. Kabul Task
edilirse , bu aslında devamı işleyecek yeniyi ortaya çıkardığınızda bir miktar ek yüke yol açar , ancak bunu sıkı bir döngüde işlemediğiniz düşünülürse bu küçük olmalıdır (çağrılar arasında on saniye bekliyorsunuz).
Eğer DoWork
gerçekte (bir döndüren içinde, yani awaitable yapılabilir fonksiyonu Task
), o zaman (muhtemelen) daha yukarıda fabrika yöntemini değişiklikler yaparak bu duruma bir almaya Func<DateTimeOffset, CancellationToken, Task>
bir yerine Action<DateTimeOffset>
, şöyle:
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;
}
Elbette, CancellationToken
yönteminizi (eğer kabul ederse) burada yapmak iyi bir uygulama olacaktır .
Bu, daha sonra DoWorkAsync
aşağıdaki imzaya sahip bir yönteme sahip olacağınız anlamına gelir :
Task DoWorkAsync(CancellationToken cancellationToken);
StartWork
Yönteme aktarılan yeni imzayı hesaba katmak için yöntemi değiştirmeniz gerekir (sadece biraz ve burada endişelerin ayrılmasını aklamazsınız), CreateNeverEndingTask
şöyle:
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
task.Post(DateTimeOffset.Now, wtoken.Token);
}