.Net2.0で非ビジュアルコンポーネントを構築しています。このコンポーネントは、非同期ソケット(BeginReceive、EndReceiveなど)を使用します。非同期コールバックは、ランタイムによって作成されたワーカースレッドのコンテキストで呼び出されます。コンポーネントユーザーはマルチスレッドについて心配する必要はありません(これが主な目標であり、私が望むものです)
コンポーネントユーザーは、任意のスレッドで非ビジュアルコンポーネントを作成できます(UIスレッドは、単純なアプリケーションの一般的なスレッドです。より深刻なアプリケーションでは、任意のワーカースレッド内にコンポーネントを作成できます)。コンポーネントは、「SessionConnected」や「DataAvailable」などのイベントをトリガーします。
問題:非同期コールバックとそこで発生するイベントのために、イベントハンドラーはワーカースレッドコンテキストで実行されます。最初にコンポーネントを作成したスレッドのコンテキストでイベントハンドラーを強制的に実行する中間レイヤーを使用したいと思います。
サンプルコード(例外処理などから削除...)
/// <summary>
/// Occurs when the connection is ended
/// </summary>
/// <param name="ar">The IAsyncResult to read the information from</param>
private void EndConnect(IAsyncResult ar)
{
// pass connection status with event
this.Socket.EndConnect(ar);
this.Stream = new NetworkStream(this.Socket);
// -- FIRE CONNECTED EVENT HERE --
// Setup Receive Callback
this.Receive();
}
/// <summary>
/// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect
/// </summary>
/// <param name="ar">The IAsyncResult that was used by BeginRead</param>
private void EndReceive(IAsyncResult ar)
{
int nBytes;
nBytes = this.Stream.EndRead(ar);
if (nBytes > 0)
{
// -- FIRE RECEIVED DATA EVENT HERE --
// Setup next Receive Callback
if (this.Connected)
this.Receive();
}
else
{
this.Disconnect();
}
}
非同期ソケットの性質上、コンポーネントを使用するすべてのアプリケーションには「If(this.InvokeRequired){...」が散らばっています。必要なのは、ユーザーがコンポーネントをドロップのようなものとして安心して使用できるようにすることだけです。 -の。
では、ユーザーがInvokeRequiredをチェックする必要なしに、イベントを発生させるにはどうすればよいでしょうか(言い換えると、最初にイベントを開始したスレッドと同じスレッドで発生したイベントを強制するにはどうすればよいですか)。
AsyncOperation、BackgroundWorkers、SynchronizingObjects、AsyncCallbacks、その他たくさんのことを読んだことがありますが、それはすべて私の頭を回転させます。
私はこれを思いついた、確かに不器用な「解決策」ですが、状況によっては失敗するようです(たとえば、静的クラスを介してWinFormsプロジェクトからコンポーネントが呼び出された場合)
/// <summary>
/// Raises an event, ensuring BeginInvoke is called for controls that require invoke
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
/// <remarks>http://www.eggheadcafe.com/articles/20060727.asp</remarks>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
Control ed = eventDelegate.Target as Control;
if ((ed != null) && (ed.InvokeRequired))
ed.Invoke(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
どんな助けでもいただければ幸いです。前もって感謝します!
編集:このスレッドによると、私の最善の策はSyncrhonizationContext.Postを使用することですが、それを自分の状況に適用する方法がわかりません。