消费者/生产者锁定GUI线程

时间:2017-12-08 05:42:08

标签: c# multithreading user-interface

我制作了一个示例Consumer / Producer线程应用程序,因此我可以学习如何正确使用它。

我希望它允许一个线程向GUI线程发送命令,用内容更新GUI。

我有它的工作,但有一个小问题。 GUI线程是我的消费者线程,所以我总是检查新命令(使用while循环)。问题是因为这个while循环,GUI永远不会显示,因为它总是停留在while循环中。请注意,字符串Queue最终将被更复杂的对象(包含数据和命令类型的对象)替换。

我不确定如何让GUI线程在不中断GUI功能的情况下使用命令。我做错了吗?

这是我的Form1.cs代码(表单只包含1个RichTextBox,用于显示名为OutputBox的输出)。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace MultiThreading
{
   class ThreadCommandQueue
   {
      public static ThreadCommandQueue instance = new ThreadCommandQueue();

      private Queue<string> m_queue;
      private Object m_lock;

      public static ThreadCommandQueue GetInstance()
      {
         return instance;
      }

      private ThreadCommandQueue()
      {
         m_queue = new Queue<string>();
         m_lock = new Object();
      }

      public void Add(
         string data_to_add)
      {
         lock (m_lock)
         {
            m_queue.Enqueue(data_to_add);
         }
      }

      public string Get()
      {
         lock (m_lock)
         {
            if (m_queue.Count > 0)
            {
               return m_queue.Dequeue();
            }

            return null;
         }
      }
   }

   public partial class Form1 : Form
   {
      public Form1()
      {
         InitializeComponent();
      }

      private void PopulateThreadCommandQueue()
      {
         int i = 0;
         while(true)
         {
            ThreadCommandQueue.GetInstance().Add("Item #: " + i.ToString());
            i++;
         }
      }

      private void Form1_Load(object sender, EventArgs e)
      {
         // Create the Command Queue....
         ThreadCommandQueue.GetInstance();

         // Create a Testing Producer Thread
         Thread ProducerThread = new Thread(PopulateThreadCommandQueue);
         ProducerThread.Start();

         // The GUI thread is the Consumer, so keep checking the CommandQueue for data...
         while(true)
         {
            string latest = ThreadCommandQueue.GetInstance().Get();
            if(latest != null)
            {
               OutputBox.Text += latest + "\n";
            }
         }
      }
   }
}

2 个答案:

答案 0 :(得分:2)

使用ConcurrentQueue。因此,从Queue添加和获取不需要锁定。

此外,您实时不会从UI线程(循环时)连续接收命令。如果您有这样的场景,请使用单独的线程来接收结果。

然后,从接收器线程,您可以使用Invoke命令更新UI,如下所示。

//This method called from receiver thread 
public void UpdateForm(Data d) 
{
   if(this.InvokeRequired) 
   {
      this.Invoke(new MethodInvoker(() => this.UpdateFormUI(r)));
   }
   else 
   {
      this.UpdateFormUI(data)
   }
}

public void UpdateFormUI(Data d)
{
   //Does actual ui update
}

答案 1 :(得分:0)

好的,这里的问题是你需要一直轮询消息循环以使GUI工作,你还需要一直轮询你的IPC命令队列以使你的命令工作,你需要这个轮询发生在同一时间在同一个线程上。

有多种方法可以解决这个问题,但最简单的方法是处理消息循环,当没有任何内容需要处理时,请执行IPC命令队列处理。对于WinForms应用程序,这将是:

public Form1()
{
   InitializeComponent();
   Application.Idle += (sender, eargs) => ProcessCommands();
}

private void ProcessCommands()
{
    while(true)
    {
      string latest = ThreadCommandQueue.GetInstance().Get();
      if(string.IsNullOrEmpty(latest)) return;
      OutputBox.Text += latest + "\n";
    }
}