消费者/生产者锁定 GUI 线程
Posted
技术标签:
【中文标题】消费者/生产者锁定 GUI 线程【英文标题】:Consumer/producer locking GUI thread 【发布时间】:2018-05-22 08:20:22 【问题描述】:我制作了一个示例消费者/生产者线程应用程序,因此我可以学习如何正确使用它。
我希望它允许一个线程向 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";
【问题讨论】:
不清楚你在问什么。如果练习的目的是显式实现消费者,则该对象必须在 UI 线程之外的线程中执行。就那么简单。对于任何事情,您都不能阻止 UI 线程。但是,UI 线程本身就是一种消费者线程;它将执行您传递给Control.Invoke()
或Control.BeginInvoke()
方法的任何委托。这种隐式使用已经存在的 UI 线程使用者实现将适用于大多数实际场景,并且可能适用于您的情况。但是您的问题根本不够清楚,无法知道
尝试使用 BlockingCollection,在您的代码中读取时会浪费 CPU。当然,在某些情况下 spinwait 被占用,但不是在这个。
【参考方案1】:
使用并发队列。因此从队列中添加和获取不需要锁定。
此外,您不会实时从 UI 线程(while 循环)连续接收命令。如果您有这种情况,请使用单独的线程来接收结果。
然后,您可以从接收者线程中使用 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
【讨论】:
不,它不是线程安全的。文档说“如果您需要同时从多个线程访问集合,请使用 ConcurrentQueue好的,这里的问题是您需要一直轮询消息循环以使 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";
【讨论】:
以上是关于消费者/生产者锁定 GUI 线程的主要内容,如果未能解决你的问题,请参考以下文章