如何在多个线程之间进行通信?

Eri*_*tas 11 c# multithreading thread-safety winforms

我正在为另一个程序编写一个插件,该程序使用本机程序打开一系列文件以从中提取一些数据.我遇到的一个问题是该过程需要很长时间,我想保持用户界面不挂.另外,我还想让用户在完成之前取消该过程.在过去,我使用后台工作者来处理这类事情,但在这种情况下,我认为BackgroundWorker不会起作用.

要通过API创建插件,我使用的可以通过继承IAPICommand接口来创建自定义命令.该接口包括Execute(应用程序app)方法.然后实例化该类,当用户在程序中唤起自定义命令时,程序将调用Execute()方法.

Execute()方法在调用时传递对当前Application对象的引用,并且该应用程序对象用于打开文件以从中提取数据.但是,应用程序实例无法在原始Execute()线程之外的线程请求时打开文档.

因此,通常UI将存在于主线程上,并且将在辅助线程上执行耗时的数据提取.但是,在这种情况下,必须在主线程上执行数据提取,并且我需要为UI创建辅助线程.

这是代码的精简版本.

class MyCommand:IAPICommand
{
    public void Execute(Application app) // method from IAPICommand
    {
        Thread threadTwo= new Thread(ShowFormMethod);
        threadTwo.Start();
    }

    public void ProcessWidget(Widget w, Application app)
    { 
        //uses an App to work some magic on C
        //app must be called from the original thread that called ExecuteCommand()
    }

    //method to open custom form on a seperatethread
    public void ShowFormMethod()
    {
      MyForm form = new MyForm();
      form.ShowDialog();  
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个流程图,显示了我认为这应该最终起作用的方式.

alt text http://dl.dropbox.com/u/113068/SOMLibThreadingDiagram.jpg

  1. 这个图表是否有意义,如果是这样,我甚至采取正确的方法来解决这个问题?
  2. 一旦主线程启动UI线程,我希望它等待用户选择要处理的小部件,或者通过关闭表单结束命令(图中的红色数字).如何使主线程等待,以及如何触发它继续处理或在UI线程结束时继续结束?我以为我可以让主线程在Monitor锁上等待.然后,UI线程将填充要处理的静态Widgets列表,然后脉冲主​​线程以触发处理.当窗体关闭时,UI线程也会激活主线程,如果要处理的窗口小部件列表为空,主线程将知道继续到命令的结尾.
  3. 如何允许主线程将窗口小部件处理的进度或完成传回UI线程(图中的黄色箭头)?我只是使用Form的BeginInvoke()方法来执行此操作吗?
  4. 如何允许UI线程取消窗口小部件处理(图中的绿色箭头)?我想我可以设置一个静态布尔标志,在处理每个小部件之前检查它?

Joh*_*ler 14

在应用程序中创建多个创建表单的线程通常是个坏主意.使这项工作并非不可能,但它比你想象的要困难得多,因为父子关系中的表单会相互发送消息,而当它们发送消息时,发送消息的表会阻塞接收处理它.

将此信息与您明确执行的线程之间的消息传递或同步相结合,并且很容易导致死锁.因此,一般情况下,最好确保为用户界面保留主线程,并在没有UI的其他线程中执行所有处理.

如果符合该设计,则后台线程可以使用Control.BeginInvoke将消息传递给UI线程,而无需等待消息处理.


SLa*_*aks 1

我假设主机应用程序是 WinForms 应用程序。

您需要在方法中保存原始线程中的SynchronizationContextExecute,然后调用其Send方法在主机的 UI 线程上执行代码。

例如:

class MyCommand:IAPICommand
{
    SynchronzationContext hostContext;
    public void Execute(Application app) // method from IAPICommand
    {
        hostContext = SynchronzationContext.Current;
        Thread threadTwo = new Thread(ShowFormMethod);
        threadTwo.Start();
    }

    public void ProcessWidget(Widget w, Application app)
    { 
        //uses an App to work some magic on C
        //app must be called from the original thread that called ExecuteCommand()
        SomeType someData = null;
        hostContext.Send(delegate { someData = app.SomeMethod(); }, null);
    }
}
Run Code Online (Sandbox Code Playgroud)