使用 System.Threading.Task 访问 winform 控件卡住了
Accessing winform control with System.Threading.Task gets stuck
新任务 class 在 WPF 中运行良好。但是在 Winforms 中,每次尝试访问 Winform 控件时总是卡住。下面的"InvokeRequired"例程已经和BackgroundWorker和Threadclasses一起工作,防止跨线程操作错误。但是,由于某种原因,当涉及到 Task 时,它会卡在 Invoke 方法上。帮助?
这是一个示例代码。我使用 NET 4.5.1 框架。
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinformTaskDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string now = DateTime.Now.ToString();
Task t = new Task(() => { setText(now); });
t.Start();
t.Wait();
}
private void setText(string text)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke(new Action(() => textBox1.Text = text)); //stuck forever here
}
else
{
textBox1.Text = text;
}
}
}
}
这只是一个假设,为什么它会永远卡在那里,但是
您的任务在 button1.clicked 方法中等待,该方法是 GUI 相关方法。
您的操作当时想要访问 UI 线程,但由于您的任务正在等待而被阻止。
我试过这样看它是否会跳回调试器,但它不会
private void setText(string text)
{
if (textBox1.InvokeRequired)
{
Action callSetText = () => setText(text);
textBox1.Invoke(callSetText);
}
else
{
textBox1.Text = text;
}
}
我以前自己试过,你不能阻塞 UI 线程然后在 WinForms 中跳回它
因此您必须删除 t.Wait()
你为什么在那里使用它?
你自己陷入了僵局。您 运行 一个任务,但使用 Task.Wait()
同步阻塞了它。此操作会阻塞 UI 线程。现在,从后台线程,您同步 post 一条消息到您刚刚阻塞的同一个 UI 消息循环。因此,陷入僵局。
这个问题的解决方案不是阻塞 Task
,而是异步等待它。虽然问题确实浮现在脑海中,但如果您只与 UI 元素交互,为什么首先要在后台线程中进行交互?
private async void button1_Click(object sender, EventArgs e)
{
string now = DateTime.Now.ToString();
await Task.Run(() => { setText(now); });
}
使用语句锁来防止同时在两个线程中更改对象值。
lock(textBox1){
//Change here the textbox value
textBox1.Text = "ok";
}
当其他线程正在使用同一个对象时,这将锁定并等待
问题是您使用 t.Wait()
锁定了 UI 线程
如果删除它,操作将会完成。您正在导致竞争条件,因为 Wait 正在等待文本设置,而文本无法设置是因为正在等待。
注释掉你就知道了。如果你需要在 t 完成后发生一些事情,你可以用
来完成
t.ContinueWith(_ => { finished()); });
新任务 class 在 WPF 中运行良好。但是在 Winforms 中,每次尝试访问 Winform 控件时总是卡住。下面的"InvokeRequired"例程已经和BackgroundWorker和Threadclasses一起工作,防止跨线程操作错误。但是,由于某种原因,当涉及到 Task 时,它会卡在 Invoke 方法上。帮助?
这是一个示例代码。我使用 NET 4.5.1 框架。
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinformTaskDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string now = DateTime.Now.ToString();
Task t = new Task(() => { setText(now); });
t.Start();
t.Wait();
}
private void setText(string text)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke(new Action(() => textBox1.Text = text)); //stuck forever here
}
else
{
textBox1.Text = text;
}
}
}
}
这只是一个假设,为什么它会永远卡在那里,但是 您的任务在 button1.clicked 方法中等待,该方法是 GUI 相关方法。
您的操作当时想要访问 UI 线程,但由于您的任务正在等待而被阻止。
我试过这样看它是否会跳回调试器,但它不会
private void setText(string text)
{
if (textBox1.InvokeRequired)
{
Action callSetText = () => setText(text);
textBox1.Invoke(callSetText);
}
else
{
textBox1.Text = text;
}
}
我以前自己试过,你不能阻塞 UI 线程然后在 WinForms 中跳回它
因此您必须删除 t.Wait()
你为什么在那里使用它?
你自己陷入了僵局。您 运行 一个任务,但使用 Task.Wait()
同步阻塞了它。此操作会阻塞 UI 线程。现在,从后台线程,您同步 post 一条消息到您刚刚阻塞的同一个 UI 消息循环。因此,陷入僵局。
这个问题的解决方案不是阻塞 Task
,而是异步等待它。虽然问题确实浮现在脑海中,但如果您只与 UI 元素交互,为什么首先要在后台线程中进行交互?
private async void button1_Click(object sender, EventArgs e)
{
string now = DateTime.Now.ToString();
await Task.Run(() => { setText(now); });
}
使用语句锁来防止同时在两个线程中更改对象值。
lock(textBox1){
//Change here the textbox value
textBox1.Text = "ok";
}
当其他线程正在使用同一个对象时,这将锁定并等待
问题是您使用 t.Wait()
锁定了 UI 线程如果删除它,操作将会完成。您正在导致竞争条件,因为 Wait 正在等待文本设置,而文本无法设置是因为正在等待。
注释掉你就知道了。如果你需要在 t 完成后发生一些事情,你可以用
来完成 t.ContinueWith(_ => { finished()); });