従来のasp.Net注意async/awaitピット
5613 ワード
最近、古いプロジェクトを変えたとき、自分では達成感のあることをしました.「プロジェクトは同期方法なのに、どうして非同期方法を使わないの?」と思っていました.そこで非同期メソッドがあり、タイプの次のコード(もちろん例を挙げて説明しましょう)
「どう見ても、問題ないみたいだけど、idによって名前を更新するんじゃないの?」
しかし、実際にテスト中に、エラーを報告しました.タイプの次のエラーです.
注意:このエラーは、非同期メソッドで同期メソッドを使用したためです.
「なぜこんな間違いを報告したのか」と疑問に思う同級生もいるかもしれません.
焦らないでください.これは「同期コンテキスト」に関連しています.
非同期プログラミングは必然的にスレッドの使用についてであり、スレッドには同期コンテキストの概念があり、個人的にはスレッド同期コンテキストがasync/awaitが最も心配している問題だと考えている.既存のプロジェクト開発ではasync/awaitを使用しようとするかもしれませんが、古いコードは同期方式です.この場合、asyncとして宣言されたメソッドを呼び出すと、デッドロックやアプリケーションのクラッシュの問題が発生する可能性があります.
注意:コンソールプログラムと.Net Coreプログラムでは、コンテキストを同期する必要はありません.
デッドロック
以上のコードはデッドロックを完璧に実現しています.デフォルトでは、Wait()が完了していないTaskの場合、現在のスレッドコンテキストがキャプチャされ、Taskが完了したときにこのコンテキストリカバリメソッドの実行が使用されます.asyncメソッド内のawait実行が完了すると、呼び出し元スレッドが存在するコンテキスト実行メソッドの残りの部分を取得しようとしますが、このコンテキストにはasyncメソッドの完了を待つスレッドが含まれています.そして彼らは互いに相手を待っていて、それからなくて、そこで死んでいます.
デッドロックの問題に対する解決策はConfigureAwait(false)を増やすことである.
awaitが完了を待つと、スレッドプールコンテキストでasyncメソッドの残りの部分を実行しようとするため、デッドロックは存在しません.
プロジェクトに同期コードがあり、多くの非同期コードがあり、非同期コードを実行する際のパラメータが同期コードによって取得されている場合、プロジェクトに上記の異常情報がある可能性が高い
資料を調べたり、庭の他の大物たちの文章を見たりして知った.
asyncメソッドが呼び出されると.awaitキーワードを使用すると、現在のスレッドがすぐにループプールを解放され、スレッドのコンテキスト情報が保存されます.await(async voidのメソッドを使用していない場合、必然的にawaitを使用することはできません)が使用されていない場合、asyncメソッドを呼び出すと、コードは引き続き実行され、実行が完了すると現在のスレッドはループプールを解放され、スレッドのコンテキスト情報は保存されません.asyncの非同期タスクの実行が完了すると、スレッドプールから残りのコードの実行を続行するスレッドが取得され、当初の呼び出し元のスレッドのコンテキスト情報が取得されます(当初の呼び出し元のスレッドがループバックプールを解放していない場合、コンテキスト情報は取得できます).問題は、呼び出し元がawaitを使用せずにスレッドがループバックプールを解放していた場合、コンテキスト情報が保持されていないため取得できなくなり、この場合、例外がオブジェクト参照をオブジェクトに設定していないインスタンスが投げ出され、テストされた異常情報が毎回現れるとは限らない.理由はスレッドの解放に関係している.呼び出し元が存在するスレッドのコンテキスト情報が存在する場合、例外は放出されません.
同期メソッドasyncメソッドによるASPを呼び出す.NETアプリケーションクラッシュ 非同期世界に入る-バカも分かち合う価値がある:ConfigureAwait(false)使用経験分かち合う
//
public Task<bool> UpdateUser(string id,string companyName)
{
var usrInfo=Db.GetUsrInfo(id);
var flag= await Db.UpdateCompanyNameAsync(usrInfo.companyId,companyName);
return flag
}
「どう見ても、問題ないみたいだけど、idによって名前を更新するんじゃないの?」
しかし、実際にテスト中に、エラーを報告しました.タイプの次のエラーです.
System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state)
System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state)
System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)
--- ---
System.Threading.Tasks.AwaitTaskContinuation.<>c.b__18_0(Object s)
System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
System.Threading.ThreadPoolWorkQueue.Dispatch()
System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
注意:このエラーは、非同期メソッドで同期メソッドを使用したためです.
「なぜこんな間違いを報告したのか」と疑問に思う同級生もいるかもしれません.
焦らないでください.これは「同期コンテキスト」に関連しています.
コンテキストの同期
非同期プログラミングは必然的にスレッドの使用についてであり、スレッドには同期コンテキストの概念があり、個人的にはスレッド同期コンテキストがasync/awaitが最も心配している問題だと考えている.既存のプロジェクト開発ではasync/awaitを使用しようとするかもしれませんが、古いコードは同期方式です.この場合、asyncとして宣言されたメソッドを呼び出すと、デッドロックやアプリケーションのクラッシュの問題が発生する可能性があります.
注意:コンソールプログラムと.Net Coreプログラムでは、コンテキストを同期する必要はありません.
デッドロック
private static async Task TestAsync()
{
await Task.Delay(1000);
//
}
public static void TestOne()
{
var task = TestAsync();
task.Wait();
}
以上のコードはデッドロックを完璧に実現しています.デフォルトでは、Wait()が完了していないTaskの場合、現在のスレッドコンテキストがキャプチャされ、Taskが完了したときにこのコンテキストリカバリメソッドの実行が使用されます.asyncメソッド内のawait実行が完了すると、呼び出し元スレッドが存在するコンテキスト実行メソッドの残りの部分を取得しようとしますが、このコンテキストにはasyncメソッドの完了を待つスレッドが含まれています.そして彼らは互いに相手を待っていて、それからなくて、そこで死んでいます.
デッドロックの問題に対する解決策はConfigureAwait(false)を増やすことである.
// await Task.Delay(1000);
await Task.Delay(1000).ConfigureAwait(false); //
awaitが完了を待つと、スレッドプールコンテキストでasyncメソッドの残りの部分を実行しようとするため、デッドロックは存在しません.
プロジェクトに同期コードがあり、多くの非同期コードがあり、非同期コードを実行する際のパラメータが同期コードによって取得されている場合、プロジェクトに上記の異常情報がある可能性が高い
資料を調べたり、庭の他の大物たちの文章を見たりして知った.
asyncメソッドが呼び出されると.awaitキーワードを使用すると、現在のスレッドがすぐにループプールを解放され、スレッドのコンテキスト情報が保存されます.await(async voidのメソッドを使用していない場合、必然的にawaitを使用することはできません)が使用されていない場合、asyncメソッドを呼び出すと、コードは引き続き実行され、実行が完了すると現在のスレッドはループプールを解放され、スレッドのコンテキスト情報は保存されません.asyncの非同期タスクの実行が完了すると、スレッドプールから残りのコードの実行を続行するスレッドが取得され、当初の呼び出し元のスレッドのコンテキスト情報が取得されます(当初の呼び出し元のスレッドがループバックプールを解放していない場合、コンテキスト情報は取得できます).問題は、呼び出し元がawaitを使用せずにスレッドがループバックプールを解放していた場合、コンテキスト情報が保持されていないため取得できなくなり、この場合、例外がオブジェクト参照をオブジェクトに設定していないインスタンスが投げ出され、テストされた異常情報が毎回現れるとは限らない.理由はスレッドの解放に関係している.呼び出し元が存在するスレッドのコンテキスト情報が存在する場合、例外は放出されません.