日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Dotnet線程取消的深度進階

取消的概念

取消則不同。

通常,取消是由其它代碼發(fā)出的命令,也就是說,是由一些代碼去請求取消,另一部分代碼的響應取消。而且,實際發(fā)生的情況,是請求代碼只是通知響應代碼,希望它能停止執(zhí)行;響應代碼會按照自己設定的方式對取消請求做出響應,有可能立即停止任務,也有可能繼續(xù)運行下去,直到一個可以停止的點,甚至可能完全忽略這個取消請求。

概念清楚了,怎么做?

取消令牌

既然是一方請求,另一方響應,那對于響應代碼來說,重要的是能夠知道并響應取消請求。

在 Dotnet 里,給出了一個東西,叫取消令牌 ( Cancellation Tokens )。這個令牌,就是請求取消的載體。

請求代碼發(fā)起取消時,實際是發(fā)起了一個對「取消令牌」的取消操作,然后,響應代碼將對這個被取消的令牌做出正確反應。

如果看到這兒有點混亂的話,看一下示例代碼:

async Task SomethingAsync(int data, CancellationToken cancellationToken)
{
var result = await FirstStepAsync(data, cancellationToken);
await SecondStepAsync(intermediateValue, cancellationToken);
}

響應代碼基本都是這個樣子。這里面,CancellationToken 就是上面說的取消令牌。

CancellationToken 可以在任何地方被設置為取消:用戶按下取消按鈕,或客戶端斷開連接,超時,等等。重要的是,當它被設置為取消時,就表示響應代碼需要處理取消了。

注意:一個 CancellationToken 只能被取消一次。一旦它被取消,就會永遠保持取消狀態(tài)。

帶有取消令牌的方法定義

上面的示例,就是一個典型的帶有取消令牌的方法定義。

按照微軟的習慣,帶有 CancellationToken 的方法有以下約定:

  • CancellationToken 通常是最后一個參數(shù)
  • 方法通常會提供一個重載,或默認參數(shù)值,以便調(diào)用者可以不提供取消令牌而直接調(diào)用

當然,這是一個非強制的約定。如果你不介意別人看著別扭,可以不管這個約定。

看幾個例子:

Task SomethingAsync(int data) => SomethingAsync(data, CancellationToken.None);

async Task SomethingAsync(int data, CancellationToken cancellationToken)
{
...
}

async Task SomethingAsync(int data, CancellationToken cancellationToken = default)
{
...
}

在這里,CancellationToken 代表任何類型或任何原因的取消。

通過 CancellationToken 參數(shù),方法聲明了自己可以響應取消。而實際上,這只是個聲明。代碼中,CancellationToken 可能會被忽略。因此,有這個聲明僅僅表示方法可能支持取消,而不是一定支持。

方法對取消的響應

上面說到了,響應代碼可以響應取消,也可以不取消。

而即使響應代碼真的去響應取消,通常也會有不同的情況。

通常來說,如果取消請求到達時,響應方法實際取消了一些工作,會拋出 OperationCanceledException 來通知調(diào)用程序;而如果取消被忽略,或者取消請求來的太晚而任務已經(jīng)完成,那響應方法會正常返回,而且不拋出 OperationCanceledException 異常。這個在微軟的基礎類庫(BCL)中,體現(xiàn)得很明顯。

大多數(shù)情況下,異常會被逐層傳出。再看一下上面的例子:

async Task SomethingAsync(int data, CancellationToken cancellationToken)
{
var result = await FirstStepAsync(data, cancellationToken);
await SecondStepAsync(intermediateValue, cancellationToken);
}

如果 FirstStepAsync 或 SecondStepAsync 拋出 OperationCanceledException,那這個異常也會從 SomethingAsync 中傳出給調(diào)用者。

這里要強調(diào)一下:看過很多代碼,在請求取消時會不拋出異常而直接返回。不要這樣做。調(diào)用者不知道這個取消是被接受,還是被忽略,會出大問題的。

一個常見的錯誤用法

在代碼 Review 時,見過好幾次這樣的情況:

async Task SomethingAsync(CancellationToken cancellationToken)
{
var test = await Task.Run(() =>
{
...
}, cancellationToken);
...
}
// 注意,這個例子的寫法是錯的。

這個有必要專門拿出來說一下。

很多人把委托和 CancellationToken 傳遞給 Task,期望在令牌取消時取消委托。注意,這個理解是錯的。

Task.Run 是對線程池的委托調(diào)度,是一個立即完成的瞬時動作。CancellationToken 在這兒的作用是取消調(diào)度這個動作,而這個動作是立即完成的,換句說說,一旦走到這一行,調(diào)度操作會立即完成,這個取消令牌也就沒有用了,會被忽略。

所以,這種情況不需要用 CancellationToken,要寫成下面的方式:

async Task SomethingAsync(CancellationToken cancellationToken)
{
var test = await Task.Run(( cancellationToken ) =>
{
...
});
...
}

寫成這樣,才是正確的表達,表達委托本身需要響應令牌。

這是一個容易搞錯的知識點,記一下。


分享標題:Dotnet線程取消的深度進階
瀏覽路徑:http://www.dlmjj.cn/article/copspgs.html