BackgroundWorkerを研究したところ、AsyncOperationとSynchronizationContextの違いは本当に大きいことがわかりました.
9483 ワード
今日BackgroundWorkerコードを研究したところ、2つのコードの書き方が一致していないことがわかり、BackgroundWorkerのバグを測定できると思って好奇心を持ってテストしました.結局microsoftが勝ったことはみんな知っていた.
次に、BackgroundWorkerクラスのReportProgressメソッドを見てみましょう.
BackgroundWorkerのWorkerThreadStartメソッド
上記の2つの方法のうち,1つはasyncOperationがnullであると判断し,1つは判断しない.asyncOperationの作成はRunWorkerAsyncメソッドで行われます.マイクロソフトのコードは厳格に書かれていると思っていたが、彼がこのように書くのはきっと道理があるに違いない.そこでasyncOperationがどのように作成されたかを深く掘り下げます.
一般に、この方法はUIスレッドで呼び出されるべきであり、asyncOperationのSynchronizationContextはWindowsFormsSynchronizationContextであり、更新および完了イベントでUIコントロールの更新に問題はありません.しかし、RunWorkerAsyncメソッドを呼び出すのはUIスレッドではないのでしょうか.この疑問を持って、私はわざと新しいスレッドを起動してBackgroundWorkerを初期化し、結果としてスレッド間の不正アクセスが発生しました.しかし、判断されていないコードのthis.asyncOperationはnullのはずですね.すべて異常ですが、私が予想したNull Exception異常ではありません.そこでasyncOperationの作成をさらに掘り下げるしかなかった.
BackgroundWorker内部ではAsyncOperationを使用しています.AsyncOperationはSynchronizationContextのパッケージであることはよく知られています.
1 UI以外のスレッドでSynchronizationContextを呼び出すとCurrentはnullを返します.
2 UI以外のスレッドでAsyncOperationManagementを呼び出すとCreateOperationはAsyncOperationを作成し、nullを返すべきではないでしょうか.
希望に反してnullにはならない.それだけだ.中に包装されているSynchronizationContextはnullになるだろう.
再び失望してもnullではありません.
真実を見てみましょう.
上記のコードを見てみると、現在のスレッドの同期コンテキストがnullの場合、作業者スレッドにSynchronizationContextが一定に設定されていますが、SynchronizationContextのPostメソッドは、エージェントイベントをスレッドプールに簡単に挿入して異歩的な目的を達成するだけです.したがって、実際にイベントコールバックを実行するスレッドはUIスレッドではありません.もちろん、スレッド間でUIコントロールに不正にアクセスしたエラーを報告します.
上記の表現をまとめます.
1,上のBackgroundWorkerのコードは明らかに厳密ではありません.2つの関数はnull判断を加える必要はありません.読者を誤導します(もちろん、マイクロソフトのコードを逆コンパイルするように言われていません--).
2,SynchronizationContextを使えばnullかどうかを判断し,AsyncOperationを使えば判断しない.
3,RunWorkerAsyncの呼び出しはUIスレッドにしか入れられません.そうしないと、コールバックをUIスレッドに封入することはできません.
4、彼らの違いを明らかにしたら、どちらを使っても問題ではありません.
次に、BackgroundWorkerクラスのReportProgressメソッドを見てみましょう.
public void ReportProgress(int percentProgress, object userState)
{
if (!this.WorkerReportsProgress) throw new InvalidOperationException("BackgroundWorker_WorkerDoesntReportProgress");
ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState);
if (this.asyncOperation != null)// asyncOperation
this.asyncOperation.Post(this.progressReporter, arg);
else
this.progressReporter(arg);
}
BackgroundWorkerのWorkerThreadStartメソッド
private void WorkerThreadStart(object argument)
{
object result = null;
Exception error = null;
bool cancelled = false;
try
{
DoWorkEventArgs e = new DoWorkEventArgs(argument);
this.OnDoWork(e);
if (e.Cancel)
cancelled = true;
else
result = e.Result;
}
catch (Exception exception2)
{
error = exception2;
}
RunWorkerCompletedEventArgs arg = new RunWorkerCompletedEventArgs(result, error, cancelled);
//
this.asyncOperation.PostOperationCompleted(this.operationCompleted, arg);
}
上記の2つの方法のうち,1つはasyncOperationがnullであると判断し,1つは判断しない.asyncOperationの作成はRunWorkerAsyncメソッドで行われます.マイクロソフトのコードは厳格に書かれていると思っていたが、彼がこのように書くのはきっと道理があるに違いない.そこでasyncOperationがどのように作成されたかを深く掘り下げます.
public void RunWorkerAsync(object argument)
{
if (this.isRunning) throw new InvalidOperationException("BackgroundWorker_WorkerAlreadyRunning");
this.isRunning = true;
this.cancellationPending = false;
// asyncOperation
this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.threadStart.BeginInvoke(argument, null, null);
}
一般に、この方法はUIスレッドで呼び出されるべきであり、asyncOperationのSynchronizationContextはWindowsFormsSynchronizationContextであり、更新および完了イベントでUIコントロールの更新に問題はありません.しかし、RunWorkerAsyncメソッドを呼び出すのはUIスレッドではないのでしょうか.この疑問を持って、私はわざと新しいスレッドを起動してBackgroundWorkerを初期化し、結果としてスレッド間の不正アクセスが発生しました.しかし、判断されていないコードのthis.asyncOperationはnullのはずですね.すべて異常ですが、私が予想したNull Exception異常ではありません.そこでasyncOperationの作成をさらに掘り下げるしかなかった.
BackgroundWorker内部ではAsyncOperationを使用しています.AsyncOperationはSynchronizationContextのパッケージであることはよく知られています.
1 UI以外のスレッドでSynchronizationContextを呼び出すとCurrentはnullを返します.
2 UI以外のスレッドでAsyncOperationManagementを呼び出すとCreateOperationはAsyncOperationを作成し、nullを返すべきではないでしょうか.
希望に反してnullにはならない.それだけだ.中に包装されているSynchronizationContextはnullになるだろう.
再び失望してもnullではありません.
真実を見てみましょう.
public static class AsyncOperationManager
{
public static AsyncOperation CreateOperation(object userSuppliedState)
{
return AsyncOperation.CreateOperation(userSuppliedState, SynchronizationContext);
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static System.Threading.SynchronizationContext SynchronizationContext
{
get
{
// , , , !
if (System.Threading.SynchronizationContext.Current == null) System.Threading.SynchronizationContext.SetSynchronizationContext(new System.Threading.SynchronizationContext());
return System.Threading.SynchronizationContext.Current;
}
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
set
{
System.Threading.SynchronizationContext.SetSynchronizationContext(value);
}
}
}
上記のコードを見てみると、現在のスレッドの同期コンテキストがnullの場合、作業者スレッドにSynchronizationContextが一定に設定されていますが、SynchronizationContextのPostメソッドは、エージェントイベントをスレッドプールに簡単に挿入して異歩的な目的を達成するだけです.したがって、実際にイベントコールバックを実行するスレッドはUIスレッドではありません.もちろん、スレッド間でUIコントロールに不正にアクセスしたエラーを報告します.
public virtual void Post(SendOrPostCallback d, object state)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
}
上記の表現をまとめます.
1,上のBackgroundWorkerのコードは明らかに厳密ではありません.2つの関数はnull判断を加える必要はありません.読者を誤導します(もちろん、マイクロソフトのコードを逆コンパイルするように言われていません--).
2,SynchronizationContextを使えばnullかどうかを判断し,AsyncOperationを使えば判断しない.
3,RunWorkerAsyncの呼び出しはUIスレッドにしか入れられません.そうしないと、コールバックをUIスレッドに封入することはできません.
4、彼らの違いを明らかにしたら、どちらを使っても問題ではありません.