In C# what is the recommended way of passing data between 2 threads?

后端 未结 9 841
长发绾君心
长发绾君心 2020-12-08 01:36

I have my main GUI thread, and a second thread running inside it\'s own ApplicationContext (to keep it alive, even when there is no work to be done). I want to call a method

相关标签:
9条回答
  • 2020-12-08 01:41

    I'm assuming some event in the GUI requires some long-running task to start which be run in the background - there are two main ways to do this. If you simply want to call a method on a different thread then you can do it by Calling Synchronous Methods Asynchronously. I usually do something like this:

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

    Be aware when using this method that the callback is executed on the background thread, so any updates to the GUI must be done using Invoke.

    Alternatively you could use shared state to communicate between threads and use an EventWaitHandle to signal updates to the shared state - in this example a method in the GUI adds work items to a queue to be handled in the background. The worker thread processes items from the queue when work becomes available.

    
    //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();
           }
       }
    }
    
    0 讨论(0)
  • 2020-12-08 01:45

    Put a loop in your second thread, that sleeps most of the time, but every [Interval] it wakes up and checks a shared variable that tells it whether to run your method or not, and if that shared boolean is set to true, then it runs a method that performs whatever task you are trying to perform... In that method, have the method gather the data required from another shared variable.

    In main GUI thread, put the data into the method parameter shared variable, then set the boolean "Run" shared variable to true...

    Inside the worker method, remember to reset the shared bool "run" variable to false when you're done, so the loop won;t run the same instance over and over...

    0 讨论(0)
  • 2020-12-08 01:47

    Use a synchronization object to signal the thread that it needs to process the new data (or the GUI's new state). One relatively simple way to do this is to use an event object. Here's a run-down of how that would work:

    1. GUI thread an 2nd thread share an event object (so they both know about it)
    2. 2nd thread typically runs in a loop of some sort, and each time it waits for the event to be signaled
    3. GUI thread signals the event when it needs the 2nd thread to do something
    4. When the 2nd thread is done, it resets the event and waits again (or exits)
    0 讨论(0)
  • 2020-12-08 01:54

    .Net already comes with a System.ComponentModel.BackgroundWorker class specifically to handle performing background tasks and communicating with a GUI. Use it.

    0 讨论(0)
  • 2020-12-08 01:58

    In effect, you have created a poor man's version of a ThreadPool. Your second thread is just sitting there doing nothing and without a fair amount of work on your part, you can't just get it to do work for you. You would have to pass delegates into a queue that your thread then takes off and executes.

    Your best bet is to do what you intended and just use the .NET ThreadPool and give it work to do.

    0 讨论(0)
  • 2020-12-08 01:58

    The convenience of Control.BeginInvoke() is hard to pass up. You don't have to. Add a new class to your project and paste this code:

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

    Here's some sample code to test it:

      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();
        }
      }
    
    0 讨论(0)
提交回复
热议问题