【问题标题】:Threading Method Question线程方法问题
【发布时间】:2010-07-23 13:40:30
【问题描述】:

我正在使用以下方法来显示一个无模式的消息框。

public void ShowMessageBox(string Message)
{
  var thread = new Thread(
    () =>
    {
      MessageBox.Show(Message);
    });
  thread.Start();
}

“() => {...}”是我以前从未见过的。此代码模式的名称是什么?

另外,thread.Start 启动线程,一旦“()=>{...}”方法完成(当消息框被确定时)它会自动关闭,对吧?如果是这样,你能指点我一些官方文档说线程自动关闭吗?

谢谢!

【问题讨论】:

    标签: c# lambda


    【解决方案1】:

    它是 lambda 运算符,读作“goes to”。 MSDN有一个很好的介绍:Lambda Expressions (C# Programming Guide)

    与您的示例有关的一个问题是您正在启动一个新线程来更新 UI,UI 本质上是单线程的,因此后台更新通常是错误的做法(除非您手动/显式检查InvokeRequired 并根据需要致电 Invoke()


    关于 UI 线程...

    在 WinForms 中,每个 FormControl 都是在特定线程(“UI 线程”)上创建的,您可以认为该线程拥有该控件(不完全正确,但一种概念化它的好方法)。从该线程更新 UI 是安全的,从另一个线程更新 UI 存在冲突和损坏的风险以及并行/异步编程的所有常见风险。

    ...那么...如何在不阻塞 UI 的情况下从后台线程安全地更新 UI?简而言之——你不能——你能做的最好的就是在更新 UI 所需的 最低限度 内阻止它。这就是InvokeRequiredInvoke() 的用武之地……

    这是一个示例:您应该能够将其放入带有按钮和文本框的新表单的代码隐藏中。

    使用方法:

    • 尝试注释掉对SetTextAsyncSafe()SetTextAsyncSafe() 的调用——同时运行它们可能会让您感到困惑,因为它们不一定按照调用顺序执行(它们是异步运行的,记得吗? )。

    • 然后在SetText() 上设置断点。您应该看到“安全”调用实际上会调用该方法两次——第一次调用将检测到InvokeRequired,并通过Invoke()'ing 第二次调用该方法以获得正确的线程。

    • SetTextAsyncUnsafe() 实际到达textBox1.Text = value; 语句时,您应该会看到抛出异常。例外情况是 InvalidOperationException 带有一条消息,指出“跨线程操作无效”——您可以在谷歌上搜索该术语以了解更多详细信息。

    代码:

    private void button1_Click(object sender, EventArgs e)
    {
      SetTextAsyncSafe("This update was made from the UI Thread by using Invoke()");
      SetTextAsyncUnsafe("This update was made directly from the background thread and can cause problems");
    }
    
    private void SetTextAsyncUnsafe(string value)
    {
      new Thread(() => SetText(value, false)).Start();
    }
    
    private void SetTextAsyncSafe(string value)
    {
      new Thread(() => SetText(value, true)).Start();
    }
    
    private void SetText(string value, bool checkInvokeRequired)
    {
      if (checkInvokeRequired) 
      {
        if (InvokeRequired) 
        {
          Invoke(new Action(() => SetText(value, checkInvokeRequired)));
          return; // early exit
        }
      }
    
      textBox1.Text = value;
    }
    

    【讨论】:

    • 我相信MessageBox.Show 可以从任何线程调用,但是是的,它看起来不太好:-)
    • 您能否编写一些代码来展示如何在我的代码中实现 InvokeRequired 和 Invoke()?这两件事我都不熟悉。
    • @Mau,我的主要目标是创建一个不会停止代码的线程。
    • @Mau -- 是的,MessageBox 可能已经很安全了 -- 但大多数 UI 工作都不是,所以我想我应该把它说出来。
    • @Mau:正确,因为每个窗口都与创建它的线程相关联。不同的窗口可以属于不同的线程(这必须是真的,否则桌面上的每个窗口都必须在同一个线程上,因此在同一个进程中)。
    【解决方案2】:

    那是一个 Lambda。在这种情况下,您将使用它来创建一个新的匿名方法,该方法将在新线程启动时运行。

    它(接近)相当于:

    public void ShowMessageBox(string Message)
    {
        var thread = new Thread(ShowBox);
        thread.Start(Message);
    }
    
    public void ShowBox(object message)
    {
        MessageBox.Show(message.ToString());
    }
    

    【讨论】:

      【解决方案3】:

      这称为 Lambda 表达式。你可以阅读更多here

      【讨论】:

        【解决方案4】:

        Lambda 表达式,C# 版本 3 功能。

        不要使用此代码。消息框需要一个父窗口,它可以确保位于其之上。它通常可以通过迭代在同一线程上创建的窗口来自行找到父级。但在这种情况下,没有其他窗口,它必须选择桌面窗口作为父窗口。

        当用户在一个应用程序窗口中工作或将焦点切换到另一个应用程序时,这会出错,消息框会消失在前景窗口后面。用户没有明显的方法可以告诉它在那里,她只会看不到它。她可能需要几个小时甚至几天才能找到它。该线程同时严重消耗资源,如果您知道此消息框需要 1 兆字节的内存,您可能永远不会考虑它。在极端情况下,您将使用 OOM 使程序崩溃。

        Windows UI 编程中的常见替代方法是由 NotifyIcon 提供的气球工具提示。或者您自己的表单,将 TopMost 属性设置为 True,这样它就不会轻易丢失。还允许您控制位置,这对于不应妨碍的“非模态”通知很重要。在表单构造函数中将该表单的 ShowWithoutActivation 属性设置为 true,这样它就不会窃取焦点。

        【讨论】:

        • 好点。我很确定 OP 在另一个问题中从我这里得到了这个想法。这是我没有考虑过的一个问题。
        【解决方案5】:

        它是一个语句 lambda

        是的,只要此匿名方法正在运行,线程就处于活动状态。由于在 MessageBox.Show() 之后没有其他语句,线程将退出,这必须是真的......如果您有疑问,请在开始前添加:

        thread.Name = "LALALA";
        

        然后调试您的应用程序。当消息框出现时,暂停执行,转到线程视图,您将看到 LALALA 正在运行。点击确定,再次暂停,应该没有“LALALA”... =)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-10-06
          • 1970-01-01
          • 2016-08-30
          • 1970-01-01
          • 2015-10-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多