我正在使用.Net 2.0构建一个非可视组件。该组件使用异步套接字(BeginReceive、EndReceive等)。异步回调在由运行时创建的工作线程上下文中被调用。组件用户不必担心多线程(这是主要目标,我想要的)。
组件用户可以在任何线程中创建我的非可视组件(UI线程只是简单应用程序的常见线程。更严肃的应用程序可以在任意工作线程内创建组件)。组件触发事件,如“SessionConnected”或“DataAvailable”。
问题:由于异步回调和其中引发的事件处理程序在工作线程上下文中执行,因此需要使用中间层来强制事件处理程序在最初创建组件的线程上下文中执行。
示例代码(剥离了异常处理等...)
由于异步套接字的特性,所有使用我的组件的应用程序都会充斥着“如果(this.InvokeRequired){...}”的代码,而我只希望用户能够无忧地使用我的组件作为一种即插即用的方式。
那么我该如何在不需要用户检查InvokeRequired的情况下引发事件(或者换句话说,如何强制在与最初触发事件的线程相同的线程中引发事件)?
我已经阅读了关于AsyncOperation、BackgroundWorkers、SynchronizingObjects、AsyncCallbacks和其他大量内容,但这些都让我头晕。
我确实想出了这个笨拙的“解决方案”,但在某些情况下似乎会失败(例如当我的组件通过静态类从WinForms项目调用时)。
组件用户可以在任何线程中创建我的非可视组件(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();
}
}
由于异步套接字的特性,所有使用我的组件的应用程序都会充斥着“如果(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
}
}
}
希望能得到任何帮助。提前感谢!
编辑: 根据this thread,我最好的选择是使用SyncrhonizationContext.Post,但我不知道如何将其应用于我的情况。