【问题标题】:Invoke event on MainThread from worker thread从工作线程调用 MainThread 上的事件
【发布时间】:2012-10-03 13:07:35
【问题描述】:

我在从主线程的辅助线程调用事件时遇到问题。事件处理程序不在主线程上执行。谁能给我一些关于我做错了什么的指示。

谢谢

namespace ThreadSyncExample
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("MainThread: " + System.Threading.Thread.CurrentThread.ManagedThreadId);

      Execute execThe = new Execute();
      execThe.FinishedThread += (src, arg) =>
      {
        //This shoould be executed on MainThread right?
        Console.WriteLine("Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
      };

      execThe.Run();
      Console.ReadKey();
    }

  }


  class Execute
  {
    public void Run()
    {
      Thread exec = new Thread(() =>
      {
        Console.WriteLine("Worker Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        OnFinishedThread();
      });

      exec.Start();
    }

    public event EventHandler FinishedThread;
    protected virtual void OnFinishedThread()
    {
      if (null != FinishedThread)
      {
        EventArgs args = new EventArgs();
        FinishedThread(this, EventArgs.Empty);
      }
    }
  }
}

【问题讨论】:

  • 您是遇到异常还是无法正常工作?
  • 我没有得到异常,但是事件处理程序没有在主线程上执行。
  • 我不太明白你为什么希望这个事件处理程序在主线程上执行。你没有以任何方式同步它。
  • OnFinishedThread是在副线程上执行的,主线程id是不会显示出来的

标签: c# multithreading thread-safety


【解决方案1】:

C# 事件基本上只是一个易于使用的委托集合,“触发”一个事件只会导致运行时循环遍历所有委托并一次触发一个。

因此,您的 OnFinishedThread 事件处理程序将在 Worker 线程上被调用。

如果你想让你的事件在主线程上,你必须Invoke()它。

编辑:

您似乎无权访问表单或 WPF(因此您也无权访问 Invoke()

因此您必须通过线程同步过程手动编组对主线程的调用。这通常是一种痛苦。

可能最简单的解决方案是简单地使用BackgroundWorker,因为这样您不再需要手动编组对主线程的调用。

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
    // call the XYZ function
    e.Result = XYZ();
};
worker.RunWorkerCompleted += (sender, e) =>
{
    // use the result of the XYZ function:
    var result = e.Result;
    // Here you can safely manipulate the GUI controls
};
worker.RunWorkerAsync();

【讨论】:

  • 你能详细说明一下吗?也许一些代码示例。我是整个 c# 线程的新手。
  • @Ciprian 请记住,大多数时候事件不应该在主线程中触发。您应该假设附加到事件的事件处理程序应该在它想要的任何线程中运行。如果它需要在主线程中运行,通常事件处理程序会运行代码,在另一个线程中调用后,返回到主线程。本指南的主要例外是 UI 相关类中的某些事件,这些事件需要经常出现在 UI 线程中,因此值得始终切换。
  • @NicolasVoron BackgroundWorker 专为在 UI 上下文中工作而设计。我不相信它能够在控制台应用程序中按预期工作。
  • @Servy,你说得对,坦克让我指出这一点。 MSDN 文档对此并不十分明确
【解决方案2】:

FinishedThread() 事件处理程序将在执行 Execute.Run() 的同一线程上执行。仅仅因为您在 main() 中定义了 FinishedThread 事件处理程序的主体,并不意味着 main() 以某种方式定义了它的执行上下文。

您可以使用一些机制来执行线程编组:

  1. 使用 system.windows.forms.control 并使用 Invoke 方法将函数调用编组回创建控件的线程。在后台,这将使用 Windows 消息循环的功能来处理实际的编组
  2. 使用同步原语手动处理编组。

不要重复已经陈述的内容,请查看此答案以获取有关编组的更多信息:

Marshall to a thread manually

【讨论】:

  • 1.不幸的是我不能使用表格。 2. 你能详细说明一下吗?我是这方面的新手。
  • @Ciprian 如果您不使用表单或任何其他 UI,那么您可能不需要在主线程中运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多