2016-05-15 42 views
0

我有幾百個設備,我需要每隔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(); 
} 

及以下

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循環火災同時執行幾百個任務,與此同時回調e Task.Delay由線程池中的線程提供服務,每個任務需要幾個ms。

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

這可能是類似於斯蒂芬·克利裏評論在這裏,但他沒有提供一種替代What it costs to use Task.Delay()?

  • 如果ReadStatus未能歸還,我想使用取消令牌取消坐在那裏等待響應的線程...這似乎不起作用。

    等待Task.Delay(50,TK)
    了Thread.Sleep(100000)//模擬設備不響應​​

  • 我仍然有大約20個工作線程活着(即使我期待CTS .Cancel()要殺死他們。

    回答

    4

    的foreach循環觸發幾百任務同時

    由於ReadStatus是同步的(我假設你不能改變這一點),而且由於每一個需要獨立,因爲他們可以阻止調用線程,然後你有數百個任務。這已經是最有效的方法。

    有沒有更好的方法?

    如果每個設備應讀每5秒鐘,然後有自己獨立的定時器每個設備可能會更好。幾個週期後,他們應該「均勻」。

    await 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觀察到它的取消標記,那麼它應該取消;如果沒有,那麼你可以做的事情就不多了。

    線程池線程應該不終止;這減少了定時器下一次觸發時的線程流失。

    +0

    謝謝。如果我在API中有一個ReadStatusAsync,那麼使用Task.Delay的當前實現是否有效? 和每個設備使用一個單獨的計時器的現貨,應該有這個想法! – RaduTomy

    +0

    @RaduTomy:我看不到需要'Task.Delay'。如果它是異步的,則調用它並「等待」;如果它是同步的(並且你需要一個單獨的線程),然後通過Task.Run調用它。如果你使用每個設備的計時器(按照建議),你不應該需要'Task.Run'。 –

    +0

    好的,我很清楚。謝啦! – RaduTomy

    0

    正如你可以看到this Microsoft example page of a cancellation tokendoWork方法檢查取消在每次循環。因此,循環必須再次開始抵消。在你的情況,當你模擬長期任務,它在運行時從不檢查取消。

    How do I cancel non-cancelable async operations?,最後說:「那麼,您可以取消不可取消的操作嗎?不可以。您可以取消等待不可取消的操作嗎?當然,只要你做的時候要非常小心......「所以它回答說我們不能取消它

    我會建議使用ThreadPool的線程,你需要每個人的開始時間和你擁有更高優先級的線程,看看其他人是否繞過了他們允許的最長時間。如果是,Thread.Interrupt()

    +0

    謝謝,非常好的答案。我會喜歡,但我沒有足夠的聲譽。 – RaduTomy