C#多线程——无控件调用
我对多线程只是有点熟悉,因为我读过多线程,但从未在实践中使用过它 我有一个使用第三方库的项目,该库通过引发事件来共享输入设备的状态。问题是,库的编写方式这些事件是从不同的线程引发的 我的应用程序不需要是多线程的,而且我遇到了很多经典的线程问题(UI控件抱怨与不同线程交互,集合在一段代码迭代时被修改,等等) 我只想把第三方库的事件返回给我的UI线程。具体而言,我认为应该发生的是: 我的类接收事件,并且处理程序正在与UI不同的线程上运行。我想检测这种情况(比如InvokeRequired),然后执行与BeginInvoke等效的操作,将控制权交还给UI线程。然后,可以在类层次结构的上层发送适当的通知,并且我的所有数据仅由一个线程处理 问题是,接收这些输入事件的类不是从控件派生的,因此没有InvokeRequired或BeginInvoke。原因是我试图将UI和底层逻辑完全分离。该类仍然在UI线程上运行,只是在类本身中没有任何UI 现在我通过破坏分离来解决这个问题。我将引用传递给控件,该控件将显示来自类的数据,并使用其调用方法。这似乎违背了分离它们的全部目的,因为现在底层类直接依赖于我的特定UI类 也许有一种方法可以保存对运行构造函数的线程的引用,然后在线程名称空间中有一些东西将执行Invoke命令C#多线程——无控件调用,c#,multithreading,invoke,C#,Multithreading,Invoke,我对多线程只是有点熟悉,因为我读过多线程,但从未在实践中使用过它 我有一个使用第三方库的项目,该库通过引发事件来共享输入设备的状态。问题是,库的编写方式这些事件是从不同的线程引发的 我的应用程序不需要是多线程的,而且我遇到了很多经典的线程问题(UI控件抱怨与不同线程交互,集合在一段代码迭代时被修改,等等) 我只想把第三方库的事件返回给我的UI线程。具体而言,我认为应该发生的是: 我的类接收事件,并且处理程序正在与UI不同的线程上运行。我想检测这种情况(比如InvokeRequired),然后执行
有办法解决这个问题吗?我的方法完全错误吗?您不需要特定的控件,任何控件(包括表单)都可以。因此,您可以将其从UI中抽象出来 处理方法可以简单地将数据存储到类的成员变量中。当您希望将线程更新为未在该线程上下文中创建的控件时,会出现交叉线程的唯一问题。因此,泛型类可以侦听事件,然后使用委托函数调用要更新的实际控件 同样,只需要调用要更新的UI控件,以使它们具有线程安全性。不久前,我写了一篇关于“”的博客文章 这篇文章更为详细,但一种非常简单(但有限)的方法的关键是在要更新的UI控件上使用匿名委托函数:
if (label1.InvokeRequired) {
label1.Invoke(
new ThreadStart(delegate {
label1.Text = "some text changed from some thread";
}));
} else {
label1.Text = "some text changed from the form's thread";
}
我希望这有帮助。InvokeRequired在技术上是可选的,但调用控件的成本相当高,因此,如果不需要,请检查确保它不会通过调用更新label1.Text
有办法解决这个问题吗
是的,解决方法是创建线程安全队列
- 事件处理程序由第三方线程调用
- 事件处理程序将某些内容(事件数据)排入您拥有的集合(例如列表)中
- 您的事件处理程序会向您自己的thead发出信号,表明集合中有数据可供其出列和处理:
- 您的线程可能正在等待某个东西(互斥体或其他);当其互斥体由事件处理程序发出信号时,它将唤醒并检查队列
- 或者,它可以周期性地(例如每秒一次或其他)唤醒并轮询队列,而不是发出信号
AsyncOperation
的实例。我用于Create
的参数通常为空,但您可以将其设置为任何值。要在该线程上调用方法,请使用该方法。use,它将指向可以与之同步的内容
这样做是对的™ 取决于应用程序的类型。对于WinForms应用程序,它将在主UI线程上运行
具体来说,使用如下方法:
SynchronizationContext context =
SynchronizationContext.Current ?? new SynchronizationContext();
context.Send(s =>
{
// your code here
}, null);
如果您正在使用WPF:
您需要对管理UI线程的Dispatcher对象的引用。然后,您可以在dispatcher对象上使用Invoke或BeginInvoke方法来调度UI线程中发生的操作
获取调度程序的最简单方法是使用Application.Current.dispatcher。这是负责主(可能是唯一)UI线程的调度程序
总而言之:
class MyClass
{
// Can be called on any thread
public ReceiveLibraryEvent(RoutedEventArgs e)
{
if (Application.Current.CheckAccess())
{
this.ReceiveLibraryEventInternal(e);
}
else
{
Application.Current.Dispatcher.Invoke(
new Action<RoutedEventArgs>(this.ReceiveLibraryEventInternal));
}
}
// Must be called on the UI thread
private ReceiveLibraryEventInternal(RoutedEventArgs e)
{
// Handle event
}
}
class-MyClass
{
//可以在任何线程上调用
公共接收图书馆事件(路由目标e)
{
if(Application.Current.CheckAccess())
{
此。接收库事件内部(e);
}
其他的
{
Application.Current.Dispatcher.Invoke(
新操作(this.ReceiveLibraryEventInternal));
}
}
//必须在UI线程上调用
私人接收库事件内部(路由目标e)
{
//处理事件
}
}
我也遇到了同样的情况。但是,在我的例子中,我不能使用SynchronizationContext.Current,因为我没有访问任何UI组件的权限,也没有回调来捕获当前的同步上下文。结果表明,如果代码当前未在Windows窗体消息泵中运行,SynchronizationContext.Current将设置为标准SynchronizationContext,它将仅在当前线程上运行Send调用,并在