使用 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()); });