目錄
經常發現需要在代碼執行中添加暫停,乍一看遠程目標或文件可能尚未準備好,所以您退後一段時間再試一次,重複執行直到目標準備好或過程被取消。在這種情況下,很容易放入Thread.Sleep(n)——但這不會響應取消觸發器,所以爲什麼不使用CancellationToken呢?
介紹
不久前,我被要求查看一項服務,該服務間歇性地在停止和關閉操作上生成異常。
該服務本身是一個簡單的設備管理組件,它使用一個任務在啓動時連接到遠程設備,進行一些更新,僅此而已。但是,當嘗試進行連接時,目標設備並不總是準備就緒,因此使用了while循環,並以調用Thread.Sleep(20000)的形式插入了暫停。
在再次嘗試之前將任務暫停20秒的想法很合理——但是20秒線程塊會間歇性地(取決於時間)導致服務管理器由於觸發關閉或停止超時而強制關閉服務,由此產生的異常將填滿日誌。
Thread.Sleep(n)無法取消——請考慮使用CancellationToken.WaitHandle.WaitOne(n)。
使用代碼
本技巧中的代碼是Thread.Sleep和CancellationToken.WaitHandle.WaitOne在任務中的行爲的一個小示例,您可以對其進行試驗
服務中實現的原始while循環如下所示:
while (!cancellationToken.IsCancellationRequested)
{
// Processing
if(connectionReady)
{
// Do its business
break;
}
// Pause for 20 seconds before trying again
Thread.Sleep(20000);
}
在此實現中,線程將被阻塞20秒——不管是否有任何取消觸發器。這是潛在的問題——根據與Thread.Sleep調用有關的停止操作的時間,服務管理器將使停止操作超時並強行終止管理服務。
暫停執行代碼,但知道取消請求很簡單:
while (!cancellationToken.IsCancellationRequested)
{
// Processing
if(connectionReady)
{
// Do its business
break;
}
// Pause for 20 seconds before trying again - now with cancellation support
var cancellationTriggered = cancellationToken.WaitHandle.WaitOne(20000);
}
這一行更改實現了與原始實現相同的20秒暫停,並且它也知道任務取消。
興趣點
如果您發現到處都在使用Thread.Sleep(n),請考慮改爲使用CancellationToken.WaitHandle.WaitOne(n)方法。這將有助於保持異常記錄的大小。