【问题标题】:Ensure new form is created on the UI thread确保在 UI 线程上创建新表单
【发布时间】:2018-05-17 08:56:52
【问题描述】:

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

据我了解-尽管可能完全不正确-但问题是有时 WorkerThread 会创建表单。此后,.InvokeRequired 将始终为真,即使随后从 UI 线程更新了表单。在后一种情况下,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);
}

【问题讨论】:

  • 这甚至不是线程安全的,您最终可能会得到 2 个或更多表单并丢失数据。
  • MyForm.InvokeRequired 毫无意义且错误。
  • 你为什么首先使用多个线程?你在解决什么问题?
  • 因为你有一个静态字段。这不是自动线程安全的。
  • 并尽可能减少静态问题。它们很容易开始,然后就会咬人。

标签: c# multithreading winforms thread-safety


【解决方案1】:

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

每个表单/对话框/用户控件都应该在 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 线程编组调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多