WinForms线程间修改

Posted

技术标签:

【中文标题】WinForms线程间修改【英文标题】:WinForms interthread modification 【发布时间】:2009-07-10 15:59:57 【问题描述】:

每当我想从另一个线程修改一个winform时,我都需要使用

->Invoke(delegate, params)

以便修改发生在winform自己的线程中。

对于每个需要修改 gui 的函数,我都需要另一个委托函数。

是否有一些方案可以让我限制所需的委托函数的数量?我有一个控制器类,可以在一个地方处理整个 gui,我曾考虑过重用委托,但味道很糟糕。

我认为我的问题可以适用于所有可以运行 winform 的语言

【问题讨论】:

【参考方案1】:

如果您使用的是 C# 3,则可以使用 lambda,而在 C# 2 中,可以使用匿名委托。当不需要重用行为时,这些简化了语法。我总是做的一件事是在表单代码中进行同步,而不是在控制器中。控制器不应该被这类“管道”问题所困扰,这些问题更具体于技术而不是控制器逻辑。

public void ResetFields()

    // use "delegate" instead of "() =>" if .Net version < 3.5
    InvokeOnFormThread(() => 
    
        firstInput.Text = Defaults.FirstInput;
        secondInput.Text = Defaults.SecondInput;
        thirdChoice.SelectedIndex = Defaults.ThirdChoice;
    );


// change Action to MethodInvoker for .Net versions less than 3.5
private void InvokeOnFormThread(Action behavior) 

    if (IsHandleCreated && InvokeRequired)
    
        Invoke(behavior);
    
    else
    
        behavior();
    

作为一种做法,将表单中的所有公共方法都称为“InvokeOnFormThread”。或者,您可以使用 AOP 拦截表单上的公共方法调用并调用“InvokeOnFormThread”,但上述方法运行良好(如果您始终如一并记得始终在表单或 UserControls 上的公共方法上执行此操作)。

【讨论】:

【参考方案2】:

看看使用现有的System.Action&lt;T&gt;System.Func&lt;T,T&gt; 代表:

control.Invoke(
    new Action<int, string>(
        (i, s) => MessageBox.Show(String.Format(s, i))), 1, "0");
int iret = (int) control.Invoke(new Func<int, int>(i1 => i1 + 1));

【讨论】:

很遗憾,但这些代表在 .net 2 中不可用。但是您可以轻松地声明它们。【参考方案3】:

Michael Meadows 的回答是一个很好的例子,说明如何集中更新 GUI 表单的逻辑。

关于调用众多委托来同步前端的性能(我们很容易沉迷于此),不久前,我编写的软件在 GUI 同步方面优于等效的 C++(本机)Windows 应用程序!这一切都归功于BeginInvokeThreadPool 类。

使用Action&lt;&gt;Func&lt;&gt; 代表和ThreadPool 类也是有益的,并考虑一般的Invoke 模式(上面由Michael 公开):

public void TheGuiInvokeMethod(Control source, string text)

   if (InvokeRequired)
      Invoke(new Action<Control, string>(TheGuiInvokeMethod, source, text);
   else
   
       // it is safe to update the GUI using the control
      control.Text = text;
   

TheGuiInvokeMethod 真正位于表单或其他控件中的位置。

【讨论】:

【参考方案4】:

我记得关闭检查并手动验证我使用的每个呼叫都是安全的。

由于我对某些线程的位置(信号量)有保证,或者因为它们调用了可以在其他进程上使用的底层 API 函数,它们中的许多可以称为跨线程。

我仍然得到了很多调用,通常是在上下文对象上,所以我可以使用 MethodInvoker。

我还在 Control.Invoke 中遇到了一个令人讨厌的错误,促使我编写自定义调用程序库。

【讨论】:

什么是错误...我从未遇到过讨厌的错误,但正如他们所说,知道是成功的一半,也许我出于无知而避免了。 :) 关闭一个调用挂起的控件会导致崩溃。只要拥有线程仍在消息循环中,我的自定义版本就会正确调用。

以上是关于WinForms线程间修改的主要内容,如果未能解决你的问题,请参考以下文章

WinForms中的进程间多线程通信(Web浏览器控件)

保持更长时间会跳得更高(C# winforms)

多线程帮助,再次(winForms)

检测是不是在 WPF 和 Winforms 中的 UI 线程上

找出从后台线程访问的 winforms 控件

WinForms 中的每个表单都有自己的线程吗?