[英]High performance async monitoring tasks
我有數百個設備,我需要每5秒檢查一次它們的狀態。
我正在使用的API包含一個阻止函數,該函數調用dll並返回單個設備的狀態
string status = ReadStatus(int deviceID); // waits here until the status is returned
上面的函數通常在幾毫秒內返回狀態,但是在某些情況下,我可能一秒鍾或更長時間都無法恢復狀態! 甚至更糟的是,一台設備可能根本不響應。
因此,我需要引入一種異步形式,以確保一個無響應的設備不會阻止其他所有受監視的設備。
我目前的方法如下
// triggers every 5 sec
public MonitorDevices_ElapsedInterval(object sender, ElapsedEventArgs elapsedEventArgs)
{
foreach (var device in lstDevices) // several hundred devices in the list
{
var task = device.ReadStatusAsync(device.ID, cts.Token);
tasks.Add(task);
}
// await all tasks finished, or timeout after 4900ms
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(4900, cts.Token));
cts.Cancel();
var devicesThatResponded = tasks.Where(t => t.Status == TaskStatus.RanToCompletion)
.Select(t => t.GetAwaiter().GetResult())
.ToList();
}
並在Device類中
public async Task ReadStatusAsync(int deviceID, CancellationToken tk)
{
await Task.Delay(50, tk);
// calls the dll to return the status. Blocks until the status is return
Status = ReadStatus(deviceID);
}
我的代碼有幾個問題
我認為這是一個巨大的潛在瓶頸。 有沒有更好的方法?
這可能類似於Stephen Cleary在這里發表的評論,但他沒有提供替代方案使用Task.Delay()的成本是多少?
萬一ReadStatus無法返回,我正在嘗試使用取消令牌來取消坐在那里等待響應的線程……這似乎不起作用。
等待Task.Delay(50,tk)
Thread.Sleep(100000)//模擬設備無響應
我仍然有大約20個工作線程處於活動狀態(即使我期望cts.Cancel()殺死它們)。
foreach循環同時觸發數百個任務
由於ReadStatus
是同步的(我假設您不能更改此設置),並且由於每個人都必須獨立,因為他們可以阻塞調用線程,所以您必須有數百個任務。 那已經是最有效的方法。
有沒有更好的方法?
如果每個設備應該讀每5秒鍾,然后有自己獨立的定時器每個設備可能會更好。 幾個周期后,它們應“均勻”。
等待Task.Delay(50,tk);
我不建議使用Task.Delay
來“蹦床”非異步代碼。 如果您希望在線程池上運行代碼,只需將其包裝在Task.Run
:
foreach (var device in lstDevices) // several hundred devices in the list
{
var task = Task.Run(() => device.ReadStatus(device.ID, cts.Token));
tasks.Add(task);
}
我正在嘗試使用取消令牌來取消坐在那里等待響應的線程……這似乎不起作用。
取消令牌不會殺死線程。 如果ReadStatus
觀察到其取消令牌,則應取消; 如果沒有,那么您將無能為力。
線程池中的線程不應該被終止; 這樣可以減少下次計時器觸發時的線程攪動。
如您在此取消標記的Microsoft示例頁中所看到的, doWork
方法正在檢查每個循環上的取消。 因此,循環必須重新開始才能取消。 就您而言,當您模擬一個長任務時,它永遠不會在運行時檢查取消。
從如何取消不可取消的異步操作? ,最后說:“那么,您可以取消不可取消的操作嗎?不可以。您可以取消對不可取消的操作的等待嗎?當然……在操作時要非常小心。” 因此它回答了我們無法取消它。
我建議的是將線程與ThreadPool一起使用,您需要每個線程的開始時間,並且您有一個較高優先級的線程,該線程可以查看其他線程是否繞過了它們的最大允許時間。 如果是這樣,請使用Thread.Interrupt()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.