C# 从UI线程中的异步组件触发事件
我正在.NET2.0中构建一个非可视组件。此组件使用异步套接字(BeginReceive、EndReceive等)。异步回调是在运行时创建的工作线程的上下文中调用的。组件用户不必担心多线程(这是我想要的主要目标) 组件用户可以在任何线程中创建我的非可视组件(UI线程只是简单应用程序的普通线程。更严重的应用程序可以在任意工作线程中创建组件)。组件触发事件,如“SessionConnected”或“DataAvailable” 问题:由于异步回调和其中引发的事件,事件处理程序在工作线程上下文中执行。我想用一个中间层,这个力 要在创建事件的线程上下文中执行的事件处理程序 首先是组件 示例代码(从异常处理等中剥离)C# 从UI线程中的异步组件触发事件,c#,user-interface,asynchronous,sockets,invokerequired,C#,User Interface,Asynchronous,Sockets,Invokerequired,我正在.NET2.0中构建一个非可视组件。此组件使用异步套接字(BeginReceive、EndReceive等)。异步回调是在运行时创建的工作线程的上下文中调用的。组件用户不必担心多线程(这是我想要的主要目标) 组件用户可以在任何线程中创建我的非可视组件(UI线程只是简单应用程序的普通线程。更严重的应用程序可以在任意工作线程中创建组件)。组件触发事件,如“SessionConnected”或“DataAvailable” 问题:由于异步回调和其中引发的事件,事件处理程序在工作线程上下文中执行。
//
///在连接结束时发生
///
///IAsyncResult从中读取信息
专用void EndConnect(IAsyncResult ar)
{
//通过事件传递连接状态
此.Socket.EndConnect(ar);
this.Stream=新的网络流(this.Socket);
//--这里的火灾事件--
//安装程序接收回调
这个.Receive();
}
///
///在数据接收完成时发生;当收到0字节时,我们可以假定连接已关闭,因此应该断开连接
///
///BeginRead使用的IAsyncResult
专用void EndReceive(IAsyncResult ar)
{
整数字节;
n字节=this.Stream.EndRead(ar);
如果(n字节>0)
{
//--此处的火灾接收数据事件--
//设置下一个接收回调
如果(此连接)
这个.Receive();
}
其他的
{
这个.Disconnect();
}
}
由于异步套接字的性质,所有使用我的组件的应用程序都充斥着“If(this.invokererequired){…”,我只想让用户能够使用我的组件,而不用担心,就像是一个插件
那么,我如何在不要求用户检查InvokeRequired的情况下引发事件(或者,换言之,我如何强制在与最初启动事件的线程相同的线程中引发事件)
我读过关于AsyncOperation、BackgroundWorkers、SynchronizingObject、AsyncCallbacks和其他很多东西,但这些都让我头晕目眩
我确实提出了这个,当然是笨拙的“解决方案”,但在某些情况下它似乎失败了(例如,当我的组件从WinForms项目通过静态类调用时)
//
///引发事件,确保为需要调用的控件调用BeginInvoke
///
///
///
/// http://www.eggheadcafe.com/articles/20060727.asp
受保护的void RaiseEvent(委托事件委托,对象[]args)
{
if(eventDelegate!=null)
{
尝试
{
控件ed=eventDelegate.Target作为控件;
如果((ed!=null)&(ed.invokererequired))
ed.Invoke(eventDelegate,args);
其他的
eventDelegate.DynamicInvoke(args);
}
捕获(例外情况除外)
{
WriteLine(例如GetType());
控制台写入线(例如消息);
//吞咽
}
}
}
任何帮助都将不胜感激。提前感谢
编辑:
据我所知,最好的办法是使用SyncrhonizationContext.Post,但我不知道如何将其应用于我的情况。也许我不理解这个问题,但在我看来,您可以在异步状态下传递对自定义对象的引用 我把下面的例子放在一起加以说明 首先我们有一个回调对象,它有两个属性——一个用于分派操作的控件和一个用于调用的操作
public class Callback
{
public Control Control { get; set; }
public Action Method { get; set; }
}
然后我有一个WinForms项目,它调用另一个线程上的一些随机代码(使用BeginInvoke),然后在代码执行完成时显示一个messagebox
private void Form1_Load(object sender, EventArgs e)
{
Action<bool> act = (bool myBool) =>
{
Thread.Sleep(5000);
};
act.BeginInvoke(true, new AsyncCallback((IAsyncResult result) =>
{
Callback c = result.AsyncState as Callback;
c.Control.Invoke(c.Method);
}), new Callback()
{
Control = this,
Method = () => { ShowMessageBox(); }
});
}
这就是您想要的吗?如果您的组件必须始终由同一线程使用,您可以执行以下操作:
public delegate void CallbackInvoker(Delegate method, params object[] args);
public YourComponent(CallbackInvoker invoker)
{
m_invoker = invoker;
}
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
if (m_invoker != null)
m_invoker(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
然后,当您从表单或其他控件实例化组件时,可以执行以下操作:
YourComponent c = new YourComponent(this.Invoke);
要在非UI工作线程上对事件进行排队,它必须具有某种工作排队机制,然后您可以提供一个具有CallbackInvoker签名的方法,以便在工作线程上对委托进行排队。我似乎找到了解决方案:
private SynchronizationContext _currentcontext
/// Constructor of my component:
MyComponent() {
_currentcontext = WindowsFormsSynchronizationContext.Current;
//...or...?
_currentcontext = SynchronizationContext.Current;
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
if (_currentcontext != null)
_currentcontext.Post(new System.Threading.SendOrPostCallback(
delegate(object a)
{
eventDelegate.DynamicInvoke(a as object[]);
}), args);
else
eventDelegate.DynamicInvoke(args);
}
}
private SynchronizationContext\u currentcontext
///我的组件的构造函数:
MyComponent(){
_currentcontext=WindowsFormsSynchronizationContext.Current;
//…还是。。。?
_currentcontext=SynchronizationContext.Current;
}
///
///引发事件,确保上下文正确
///
///
///
受保护的void RaiseEvent(委托事件委托,对象[]args)
{
if(eventDelegate!=null)
{
如果(_currentcontext!=null)
_currentcontext.Post(新的System.Threading.SendOrPostCallback(
委托(对象a)
{
DynamicInvoke(作为对象[])的事件代理;
}),args);
其他的
eventDelegate.DynamicInvoke(args);
}
}
我仍在测试,但它似乎运行良好。好的;因此,我在阅读了更多内容后得出以下结论:
public class MyComponent {
private AsyncOperation _asyncOperation;
/// Constructor of my component:
MyComponent() {
_asyncOperation = AsyncOperationManager.CreateOperation(null);
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
_asyncOperation.Post(new System.Threading.SendOrPostCallback(
delegate(object argobj)
{
eventDelegate.DynamicInvoke(argobj as object[]);
}), args);
}
}
}
公共类MyComponent{
专用异步操作\u异步操作;
///我的组件的构造函数:
MyComponent(){
_asyncOperation=AsyncOperationManager.CreateOperation(空);
}
///
YourComponent c = new YourComponent(this.Invoke);
private SynchronizationContext _currentcontext
/// Constructor of my component:
MyComponent() {
_currentcontext = WindowsFormsSynchronizationContext.Current;
//...or...?
_currentcontext = SynchronizationContext.Current;
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
if (_currentcontext != null)
_currentcontext.Post(new System.Threading.SendOrPostCallback(
delegate(object a)
{
eventDelegate.DynamicInvoke(a as object[]);
}), args);
else
eventDelegate.DynamicInvoke(args);
}
}
public class MyComponent {
private AsyncOperation _asyncOperation;
/// Constructor of my component:
MyComponent() {
_asyncOperation = AsyncOperationManager.CreateOperation(null);
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
_asyncOperation.Post(new System.Threading.SendOrPostCallback(
delegate(object argobj)
{
eventDelegate.DynamicInvoke(argobj as object[]);
}), args);
}
}
}