簡體   English   中英

高性能異步監控任務

[英]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);
}

我的代碼有幾個問題

  1. foreach循環同時觸發數百個任務 ,其中Task.Callback的回調由線程池中的線程提供服務,每個任務花費幾毫秒。

我認為這是一個巨大的潛在瓶頸。 有沒有更好的方法?

這可能類似於Stephen Cleary在這里發表的評論,但他沒有提供替代方案使用Task.Delay()的成本是多少?

  1. 萬一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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM