.net - 从多个线程安全地更新 WinForms 列表控件
问题描述
我有一个 .NET WinForms 应用程序,我需要在其中显示来自同时执行的Thread类的多个实例的消息。消息必须显示在列表控件中(一般情况下为列表框或多列网格)。当用户单击表单的关闭按钮时,线程必须正确终止。
假设我们有一个名为 listBox1 的ListBox控件,它显示来自线程的消息。这是我的问题的表单代码的简化版本:
public partial class Form1 : Form
{
Thread fThread1, fThread2, fThread3;
int fMsgNo1, fMsgNo2, fMsgNo3;
CancellationTokenSource fCTS = new CancellationTokenSource();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
fThread1 = new Thread(new ParameterizedThreadStart(Thread1Body));
fThread1.Start(fCTS.Token);
fThread2 = new Thread(new ParameterizedThreadStart(Thread2Body));
fThread2.Start(fCTS.Token);
fThread3 = new Thread(new ParameterizedThreadStart(Thread3Body));
fThread3.Start(fCTS.Token);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
fCTS.Cancel();
fThread1.Join();
fThread2.Join();
fThread3.Join();
fCTS.Dispose();
}
private void Thread1Body(object data)
{
CancellationToken token = (CancellationToken)data;
while (true)
{
for (int i = 0; i < 5; i++)
{
if (token.IsCancellationRequested)
return;
Thread.Sleep(100);
}
fMsgNo1++;
listBox1.BeginInvoke(
new AddMessageToListDelegate(AddMessageToList),
new object[] { $"Thread 1 posted message {fMsgNo1}"});
}
}
private void Thread2Body(object data)
{
CancellationToken token = (CancellationToken)data;
while (true)
{
for (int i = 0; i < 10; i++)
{
if (token.IsCancellationRequested)
return;
Thread.Sleep(100);
}
fMsgNo2++;
listBox1.BeginInvoke(
new AddMessageToListDelegate(AddMessageToList),
new object[] { $"Thread 2 posted message {fMsgNo2}" });
}
}
private void Thread3Body(object data)
{
CancellationToken token = (CancellationToken)data;
while (true)
{
for (int i = 0; i < 20; i++)
{
if (token.IsCancellationRequested)
return;
Thread.Sleep(100);
}
fMsgNo3++;
listBox1.BeginInvoke(
new AddMessageToListDelegate(AddMessageToList),
new object[] { $"Thread 3 posted message {fMsgNo3}" });
}
}
private delegate void AddMessageToListDelegate(string message);
private void AddMessageToList(string message)
{
listBox1.Items.Insert(0, message);
}
}
看来,这个版本的代码工作正常,但我不完全确定。困扰我的问题是这段代码的上一版本使用Invoke()方法调用而不是BeginInvoke()从工作线程更新UI线程中的列表框,这在用户点击时造成了一种阻塞关闭按钮。我发现在这种情况下,其中一个工作线程可能一直在无休止地等待用 调用的方法的执行结束Invoke
。结果,其中一个线程永远不会结束,并且应用程序挂在表单的 Closing 事件处理程序中的Thread.Join()调用之一上。如果我们使用,这个问题也会持续存在Control.BeginInvoke()
吗?
如果这个问题根本无法解决这个问题Thread
,任何人都可以建议一个更好、更强大的模板来实现我所需要的吗?是的,我知道有诸如BackgroundWorker类之类的东西可用于此任务,但我想知道如何使代码Thread
首先为该类正常工作。
解决方案
推荐阅读
- mysql - 在mysql中的列中拆分日期
- arrays - 如何订购数据验证列表
- windows - Windows 加密 API:下一代 (CNG) 是否受益于 Windows Defender Credential Guard?
- json - 如何将一种形式的 json 模式转换为 cerberus 支持的另一种形式?
- c# - 获取N级嵌套列表视图中项目的索引c#UWP
- java - 如何在不读取整个 InputStream 的情况下使 Wiremock 响应?
- c# - 如何从 first 到 end 方法获取所有先前调用的方法
- reactjs - 如何实施第三方脚本以使用提要填充页面?
- html - CSS Flex如何始终将孩子对齐到中心
- python-3.x - 为什么在一个简单的循环中,我使用 python 丢失了 sqlite3 表的 fetchAll 值?