Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C#中,建议在两个线程之间传递数据的方式是什么?_C#_Multithreading - Fatal编程技术网

在C#中,建议在两个线程之间传递数据的方式是什么?

在C#中,建议在两个线程之间传递数据的方式是什么?,c#,multithreading,C#,Multithreading,我有我的主GUI线程,第二个线程在它自己的ApplicationContext中运行(即使没有工作要做,也要保持它的活力)。我想在GUI线程的第二个线程上调用一个方法,但如果我只调用thread.method();它似乎在我的主GUI线程上运行,并导致我的GUI变得无响应。在不同线程上调用方法的最佳方式是什么 更新: 我真正想做的是在两个线程之间通信,而不是与GUI通信。GUI恰好是需要与我的第二个线程通信的线程之一 更新#2: 好吧,我一定错过了什么。我创建了一个事件和一个委托,并让我的工作线

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

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

更新#2:
好吧,我一定错过了什么。我创建了一个事件和一个委托,并让我的工作线程订阅该事件。但是当我调用Invoke(MyEvent)时;从我的GUI线程开始,工作线程所做的工作最终在GUI线程上完成,并挂起GUI线程,直到完成处理。我试图做的事情是否可能,而不需要对静态对象进行轮询?

实际上,您创建了一个穷人版本的线程池。你的第二个线程只是坐在那里什么也不做,如果你没有大量的工作,你就不能让它为你工作。您必须将委托传递到一个队列中,然后线程将启动并执行该队列


您的最佳选择是按照您的意愿执行,只需使用.NET线程池并为其提供工作。

使用同步对象向线程发出处理新数据(或GUI的新状态)所需的信号。一种相对简单的方法是使用事件对象。下面是一个如何工作的概述:

  • GUI线程第二个线程共享一个事件对象(因此他们都知道)
  • 第二个线程通常在某种类型的循环中运行,每次它都会等待事件发出信号
  • GUI线程在需要第二个线程执行某些操作时发出事件信号
  • 当第二个线程完成时,它重置事件并再次等待(或退出)

  • Net已经提供了一个
    类,专门用于处理执行后台任务和与GUI通信。使用它。

    在第二个线程中放置一个循环,该循环大部分时间处于休眠状态,但每[Interval]它都会唤醒并检查一个共享变量,该变量告诉它是否运行方法,如果该共享布尔值设置为true,则它会运行一个方法,该方法执行您试图执行的任何任务。。。在该方法中,让该方法从另一个共享变量收集所需的数据

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


    在worker方法中,记住在完成时将共享bool“run”变量重置为false,这样循环就赢了;不要反复运行同一个实例

    我假设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);
    }
    
    使用此方法时请注意,回调是在后台线程上执行的,因此对GUI的任何更新都必须使用Invoke完成

    或者,您可以使用共享状态在线程之间进行通信,并使用发送信号更新共享状态——在本例中,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();
           }
       }
    }
    

    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();
        }
      }
    

    哇,我真不敢相信人们怎么会懒得读这个问题

    不管怎样,这就是我所做的

  • 创建“消息”类。这将存储您要共享的所有信息
  • 为每个线程创建一个队列。使用同步锁(C#lock)对其进行读/写操作
  • 当您想要与线程对话时,通过将消息添加到队列,向其发送一个消息对象,其中包含它所需的所有信息的副本
  • 然后,工作线程可以从队列中读取消息,按顺序读取和处理每条消息。没有消息时,只需睡觉
  • 确保两个线程之间不共享对象。一旦GUI线程将消息粘贴到队列中,GUI线程就不再拥有该消息。它不能包含对消息的引用,否则您将陷入麻烦

    这不会给您提供尽可能好的性能,但对于大多数应用程序来说已经足够好了。更重要的是,这将使犯错误变得更加困难


    更新:不要使用同步锁和队列。而是使用ConcurrentQueue,它将自动为您处理任何锁定。您将获得更好的性能,并且不太可能出错。

    您可以使用事件或Grauenwolf所说的消息提示。我将我的每个线程包装为一个管理单例,从那里您可以轻松地实现这两个线程。你甚至可以用穷人的公共财产来交换一些东西


    您还可以实现一个状态机,每个线程可以互相监视,而不是传递消息,Dude,阅读Albahari的.Net线程免费电子书。 我已经用任何方式连接到它了,所以这不是插头。 我读过,让我的同事读过,我用过很多次

    我建议创建一个producer/consumer类,在这个类中可以启动一个等待(非阻塞)的线程,将任务排队到它的队列中,并发出信号让它开始工作


    只需谷歌就可以了。

    我不是想从第二个线程与GUI通信,我是想从GUI与我的线程通信。“与GUI通信”意味着双向,GUI可以调用public m