确保在UI线程

时间:2018-05-17 08:56:52

标签: c# multithreading winforms thread-safety

我有多个线程写入日志表单。无论第一条消息是什么线程都会在运行中创建表单。我对每个表单更新使用MyForm.InvokeRequired / .Invoke(…),但是代码有时会与状态 .WaitForWaitHandle 中的主线程发生死锁。

根据我的理解 - 可能完全不合适 - 问题是有时工人线程会创建表单。此后,即使随后从UI线程更新表单,.InvokeRequired也将始终为真。在后一种情况下,UI线程可能正在等待自己?

我希望我可以通过在UI线程本身上创建表单来解决这个问题,但是当 MyForm == null 时,我不知道从哪个控件开始。

注意:后台有一个多线程API,用于接收/发送/处理消息,我需要跟踪这些消息。

简化示例:

public static LogForm MyForm;

public static void AddLogLine(string inText) // is called from multiple threads
{
    if (MyForm == null) MyForm = new LogForm();

    if (MyForm.InvokeRequired)
        MyForm.Invoke(new Action<LogForm, string>(_appendText), MyForm ,inText); // deadlock, UI thread state: .WaitForWaitHandle
    else
        _appendText(MyForm ,inText);
}

1 个答案:

答案 0 :(得分:0)

我建议您阅读有关同步上下文的内容: https://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I

每个表单/ dialog / usercontrol都应该在UI线程上初始化,实现这一目的的一种方法是创建主表单的公共静态引用:

public static Form1 StaticReference;

在构造函数上设置它:

public Form1()
{
     StaticReference = this;
}

然后在其他地方编写代码:

public static LogForm MyForm;
public static void AddLogLine(string inText) // is called from multiple threads
{
    <namespace>.StaticReference.BeginInvoke((MethodInvoker)delegate
    {
      if (MyForm == null) 
         MyForm = new LogForm();

      Do whatever you want with MyForm within this block.
    });
}

您找到的任何解决方案仍然需要以某种方式通过UI线程来编组调用。