Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#多线程——无控件调用_C#_Multithreading_Invoke - Fatal编程技术网

C#多线程——无控件调用

C#多线程——无控件调用,c#,multithreading,invoke,C#,Multithreading,Invoke,我对多线程只是有点熟悉,因为我读过多线程,但从未在实践中使用过它 我有一个使用第三方库的项目,该库通过引发事件来共享输入设备的状态。问题是,库的编写方式这些事件是从不同的线程引发的 我的应用程序不需要是多线程的,而且我遇到了很多经典的线程问题(UI控件抱怨与不同线程交互,集合在一段代码迭代时被修改,等等) 我只想把第三方库的事件返回给我的UI线程。具体而言,我认为应该发生的是: 我的类接收事件,并且处理程序正在与UI不同的线程上运行。我想检测这种情况(比如InvokeRequired),然后执行

我对多线程只是有点熟悉,因为我读过多线程,但从未在实践中使用过它

我有一个使用第三方库的项目,该库通过引发事件来共享输入设备的状态。问题是,库的编写方式这些事件是从不同的线程引发的

我的应用程序不需要是多线程的,而且我遇到了很多经典的线程问题(UI控件抱怨与不同线程交互,集合在一段代码迭代时被修改,等等)

我只想把第三方库的事件返回给我的UI线程。具体而言,我认为应该发生的是:

我的类接收事件,并且处理程序正在与UI不同的线程上运行。我想检测这种情况(比如InvokeRequired),然后执行与BeginInvoke等效的操作,将控制权交还给UI线程。然后,可以在类层次结构的上层发送适当的通知,并且我的所有数据仅由一个线程处理

问题是,接收这些输入事件的类不是从控件派生的,因此没有InvokeRequired或BeginInvoke。原因是我试图将UI和底层逻辑完全分离。该类仍然在UI线程上运行,只是在类本身中没有任何UI

现在我通过破坏分离来解决这个问题。我将引用传递给控件,该控件将显示来自类的数据,并使用调用方法。这似乎违背了分离它们的全部目的,因为现在底层类直接依赖于我的特定UI类

也许有一种方法可以保存对运行构造函数的线程的引用,然后在线程名称空间中有一些东西将执行Invoke命令


有办法解决这个问题吗?我的方法完全错误吗?

您不需要特定的控件,任何控件(包括表单)都可以。因此,您可以将其从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调用,并在