The new Task class works great in WPF. However in Winforms, it is always stuck every time it tries to access a Winform control. The "InvokeRequired" routine below that has been working with BackgroundWorker and Thread classes that prevents cross-thread operation error. However, for some reason when it comes to Task, it gets stuck on the Invoke method. Help ?
Here is a sample code. I use NET 4.5.1 framework.
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;
}
}
}
}
答案 0 :(得分:5)
你自己陷入僵局。您运行任务但使用Task.Wait()
同步阻止它。此操作会阻止UI线程。现在,从后台线程,您同步将消息发布到刚刚阻止的同一UI消息循环。因此,陷入僵局。
对此的解决方案不是阻止Task
,而是异步等待它。虽然问题确实存在,但如果您只是与UI元素进行交互,那么为什么首先要在后台线程中进行?
private async void button1_Click(object sender, EventArgs e)
{
string now = DateTime.Now.ToString();
await Task.Run(() => { setText(now); });
}
答案 1 :(得分:0)
它只是一个假设,为什么它会永远停留在那里但是 你的任务在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()
你为什么在那里使用它?
答案 2 :(得分:0)
使用语句锁定来防止同时更改两个线程中的对象值。
lock(textBox1){
//Change here the textbox value
textBox1.Text = "ok";
}
当已经有其他线程正在使用同一个对象
时,这将锁定并等待答案 3 :(得分:0)
问题是您是否使用t.Wait()
锁定UI线程如果删除该操作,则操作将完成。您正在导致竞争条件,因为Wait正在等待设置文本并且因为Wait正在发生而无法设置文本。
评论出来,你会看到。如果你需要在完成后发生一些事情,你可以用
来完成 t.ContinueWith(_ => { finished()); });