在C#中,在两个线程之间传递数据的推荐方法是什么?

我有我的主GUI线程,第二个线程在它自己的ApplicationContext中运行(以保持它活着,即使没有工作要做)。 我想从我的GUI线程调用我的第二个线程上的方法,但是如果我只是调用thread.Method(); 它似乎在我的主GUI线程上运行并导致我的GUI无响应。 在不同线程上调用方法的最佳方法是什么?

更新:我真正想要做的是在2个线程之间进行通信,而不是与GUI通信。 GUI恰好是需要与我的第二个线程进行通信的线程之一。

更新#2:好的,我一定是错过了什么。 我创建了一个事件和一个委托,并让我的工作线程订阅了该事件。 但是当我调用Invoke(MyEvent)时; 从我的GUI线程中,工作线程最终在GUI线程上工作并挂起GUI线程直到它完成处理。 在没有轮询静态对象的情况下,我正在尝试做什么?

.Net已经附带了一个System.ComponentModel.BackgroundWorker类,专门用于处理执行后台任务和与GUI通信。 用它。

哇,我简直不敢相信人们怎么也不费心去读这个问题。

无论如何,这就是我的工作。

  1. 创建“消息”类。 这会存储您要共享的所有信息。
  2. 为每个线程创建一个队列。 使用SyncLock(C#锁)对其进行读/写。
  3. 当你想与一个线程交谈时,通过将消息添加到队列中,向它发送一个消息对象,其中包含所需信息的副本
  4. 然后,工作线程可以从队列中读取,按顺序读取和处理每条消息。 没有消息时,只需睡觉。

确保不要在两个线程之间共享对象。 一旦您的GUI线程在队列中粘贴消息,GUI线程就不再拥有该消息。 它不能保留对该消息的引用,否则您将陷入麻烦。

这不会为您提供最佳性能,但对于大多数应用程序来说它都足够好。 更重要的是,它会使犯错误更加困难。

更新:不要使用SyncLock和队列。 而是使用ConcurrentQueue,它将自动为您处理任何锁定。 你会获得更好的表现并且不太可能犯错误。

实际上,你已经创建了一个穷人的ThreadPool版本。 你的第二个主题就是坐在那里什么都不做,而你没有相当多的工作,你不能让它为你工作。 您必须将委托传递到您的线程然后起飞并执行的队列中。

最好的办法是按照你的意图行事,只需使用.NET ThreadPool就可以了。

Dude,阅读Albahari的.Net线程免费电子书。 我通过任何方式连接到它,所以这不是插头。 我读过它,让我的同事读过它,我已经多次使用它了。

我建议创建一个生产者/消费者类,在这里你可以启动一个等待(非阻塞)的线程,将任务排入队列,并发出信号以开始工作。

只是谷歌为它。

我假设GUI中的某些事件需要一些长时间运行的任务才能在后台运行 – 有两种主要方法可以做到这一点。 如果您只想在不同的线程上调用方法,则可以通过异步调用同步方法来实现 。 我通常做这样的事情:

 //delegate with same prototype as the method to call asynchrously delegate void ProcessItemDelegate(object item); //method to call asynchronously private void ProcessItem(object item) { ... } //method in the GUI thread private void DoWork(object itemToProcess) { //create delegate to call asynchronously... ProcessItemDelegate d = new ProcessItemDelegate(this.ProcessItem); IAsyncResult result = d.BeginInvoke(itemToProcess, new AsyncCallback(this.CallBackMethod), d); } //method called when the async operation has completed private void CallbackMethod(IAsyncResult ar) { ProcessItemDelegate d = (ProcessItemDelegate)ar.AsyncState; //EndInvoke must be called on any delegate called asynchronously! d.EndInvoke(ar); } 

在使用此方法时,请注意在后台线程上执行回调,因此必须使用Invoke完成对GUI的任何更新。

或者,您可以使用共享状态在线程之间进行通信,并使用EventWaitHandle来指示对共享状态的更新 – 在此示例中,GUI中的方法将工作项添加到队列中以在后台处理。 当工作可用时,工作线程处理队列中的项目。

 //shared state private Queue workQueue; private EventWaitHandle eventHandle; //method running in gui thread private void DoWork(Item itemToProcess) { //use a private lock object instead of lock... lock(this.workQueue) { this.workQueue.Add(itemToProcess); this.eventHandle.Set(); } } //method that runs on the background thread private void QueueMonitor() { while(keepRunning) { //if the event handle is not signalled the processing thread will sleep here until it is signalled or the timeout expires if(this.eventHandle.WaitOne(optionalTimeout)) { lock(this.workQueue) { while(this.workQueue.Count > 0) { Item itemToProcess = this.workQueue.Dequeue(); //do something with item... } } //reset wait handle - note that AutoResetEvent resets automatically this.eventHandle.Reset(); } } } 

Control.BeginInvoke()的便利性很难传递。 你不必。 在项目中添加一个新类并粘贴以下代码:

 using System; using System.Threading; using System.Windows.Forms; public partial class frmWorker : Form { public frmWorker() { // Start the worker thread Thread t = new Thread(new ParameterizedThreadStart(WorkerThread)); t.IsBackground = true; t.Start(this); } public void Stop() { // Synchronous thread stop this.Invoke(new MethodInvoker(stopWorker), null); } private void stopWorker() { this.Close(); } private static void WorkerThread(object frm) { // Start the message loop frmWorker f = frm as frmWorker; f.CreateHandle(); Application.Run(f); } protected override void SetVisibleCore(bool value) { // Shouldn't become visible value = false; base.SetVisibleCore(value); } } 

这是一些测试它的示例代码:

  public partial class Form1 : Form { private frmWorker mWorker; public Form1() { InitializeComponent(); mWorker = new frmWorker(); } private void button1_Click(object sender, EventArgs e) { Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); mWorker.BeginInvoke(new MethodInvoker(RunThisOnThread)); } private void RunThisOnThread() { Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); } private void button2_Click(object sender, EventArgs e) { mWorker.Stop(); } } 

使用同步对象来通知线程它需要处理新数据(或GUI的新状态)。 一种相对简单的方法是使用事件对象。 这是一个如何工作的破败:

  1. GUI线程第二个线程共享一个事件对象(所以他们都知道它)
  2. 第二个线程通常在某种循环中运行,并且每次等待事件发出信号
  3. 当需要第二个线程执行某些操作时,GUI线程会向事件发出信号
  4. 第二个线程完成后,它会重置事件并再次等待(或退出)

在第二个线程中放置一个循环,大部分时间都会hibernate,但是每个[Interval]它都会唤醒并检查一个共享变量,告诉它是否运行你的方法,如果那个共享的boolean设置为true,那么它运行一个方法来执行您尝试执行的任何任务…在该方法中,让方法从另一个共享变量中收集所需的数据。

在主GUI线程中,将数据放入方法参数共享变量中,然后将布尔“Run”共享变量设置为true …

在worker方法内部,记得在完成后将共享的bool“run”变量重置为false,这样循环就赢了; t反复运行同一个实例……

你可以使用事件或Grauenwolf说 – 一个消息提示。 我将每个线程包装为一个管理单例,从那里你可以轻松实现。 你甚至可以做穷人的公共财产翻转位。

您还可以实现状态机,而不是传递消息,每个线程可以相互监视