Senin bu doğrudan VUK Tasks
ile aşağı ve kirli almak gerekir sanmıyorum Tia. Yeni başlayanlar için ConcurrentQueue
(varsayılan) etrafında BlockingCollection
(varsayılan) BlockingCollection
üzerinde işlem görmesi gereken kimlikleri saklamak için ayarlanmış hiçbir BoundedCapacity
ayarlayın.
// Setup the blocking collection somewhere when your process starts up (OnStart for a Windows service)
BlockingCollection<string> idsToProcess = new BlockingCollection<string>();
Oradan sadece BlockingCollection::GetConsumingEnumerable
döndü numaralandırma üzerinde Parallel::ForEach
kullanmak. ForEach
numaralı telefondan, ParallelOptions::MaxDegreeOfParallelism
'unuzu kuracaksınız. ForEach
'un gövdesinin içinde saklı yordamınızı gerçekleştireceksiniz.
Şimdi, saklı yordam yürütme işlemi tamamlandıktan sonra, yürütmeyi en az iki saniye olarak yeniden programlamak istemediğinizi söylüyorsunuz. Sorun değil, bir geri bildirimde bulunan System.Threading.Timer
numaralı programı, geri bildirimde bulunan geri bildirimde kimliği BlockingCollection
'a geri ekleyeceksiniz. Kapatma işlemi sırasında
Parallel.ForEach(
idsToProcess.GetConsumingEnumerable(),
new ParallelOptions
{
MaxDegreeOfParallelism = 4 // read this from config
},
(id) =>
{
// ... execute sproc ...
// Need to declare/assign this before the delegate so that we can dispose of it inside
Timer timer = null;
timer = new Timer(
_ =>
{
// Add the id back to the collection so it will be processed again
idsToProcess.Add(id);
// Cleanup the timer
timer.Dispose();
},
null, // no state, id wee need is "captured" in the anonymous delegate
2000, // probably should read this from config
Timeout.Infinite);
}
Son olarak, enumerable durdurma engelleme ve tam ve Parallel ile işleniyor böylece BlockingCollection::CompleteAdding
çağırır :: ForEach çıkılacak. Bu bir Windows servisi olsaydı, örneğin bunu OnStop
'da yapardınız. Güncelleme
// When ready to shutdown you just signal you're done adding
idsToProcess.CompleteAdding();
Sen herhangi bir noktada kimlikleri büyük miktarda işlem olabilir yorumunuzda geçerli bir endişe kaldırdı ve kimliği başına bir zamanlayıcı çok fazla havai olacağını korku .Buna kesinlikle katılıyorum. Yani eşzamanlı olarak ID'lerin büyük bir listesiyle uğraşmanız durumunda, tek bir kısa aralıklı zamanlayıcı tarafından izlenen "uyku" kimliklerini tutmak için başka bir kuyruğu kullanmak için bir kronometre başına zamanlayıcı kullanmaktan değiştirirdim. İlk, üzerine uykuda kimliklerini yerleştirmek için bir ConcurrentQueue
gerekir:
ConcurrentQueue<Tuple<string, DateTime>> sleepingIds = new ConcurrentQueue<Tuple<string, DateTime>>();
Şimdi, açıklama amacıyla burada iki parçasını Tuple
kullanıyorum, ama daha kesinlikle yazılı oluşturmak isteyebilirsiniz Daha iyi okunabilirlik için (ya da en azından using
bildirimi ile en az takma) yapı. Tuple, kimliği ve kuyruğa koyulduğunu gösteren bir DateTime'a sahiptir.
Sonra Timer wakeSleepingIdsTimer = new Timer(
_ =>
{
DateTime utcNow = DateTime.UtcNow;
// Pull all items from the sleeping queue that have been there for at least 2 seconds
foreach(string id in sleepingIds.TakeWhile(entry => (utcNow - entry.Item2).TotalSeconds >= 2))
{
// Add this id back to the processing queue
idsToProcess.Enqueue(id);
}
},
null, // no state
Timeout.Infinite, // no due time
100 // wake up every 100ms, probably should read this from config
);
basitçe yerine aşağıdakilerden her biri için bir zamanlayıcı ayarlama yapmak Parallel::ForEach
değiştirecek:
Şimdi de kurulum için bu kuyruğa izleyecek zamanlayıcı isteyeceksiniz
(id) =>
{
// ... execute sproc ...
sleepingIds.Enqueue(Tuple.Create(id, DateTime.UtcNow));
}
Reaktif Programlama –