WinForm Task.Wait:为什么它会阻塞用户界面(UI)?

huangapple go评论83阅读模式
英文:

WinForm Task.Wait : why is it blocking the UI?

问题

我猜一般情况下不应该这样做,但我想了解为什么这段代码会导致用户界面(UI)被阻塞,而不会从 await taskMain 返回:

using System.Diagnostics;

namespace TasksTest
{
    public partial class FormMain : Form
    {
        public FormMain()
        {
            InitializeComponent();
        }

        private void buttonTest1_Click(object sender, EventArgs e)
        {
            try
            {
                DoTest1Async().Wait();
            }
            catch (Exception exc) 
            {
                MessageBox.Show(exc.ToString());
            }
        }

        private static async Task DoTest1Async()
        {
            Task taskMain = Task.Run(() =>
            {
                Debug.WriteLine("Test #1 completed");
            });
            await taskMain;
        }
    }
}

如果我使用 await 而不是 Wait(),它可以完美地工作。我想知道是否有一种方式可以同步执行 DoTest1Async 而不会阻塞用户界面。

------- 编辑 ----------

在理解了调用 Wait 方法会阻塞 UI 线程并且不允许已完成的任务 taskMain 返回到 UI 线程上下文并恢复执行之后...为什么下面的代码替代方案不会以同样的方式阻塞 UI 线程呢?我仍然不明白为什么如果我在另一个线程中启动代码,让 UI 线程在 thread.Join 处被阻塞,taskMain 可以返回,但在简单的 Wait() 指令中却不能返回。这听起来很奇怪...也许 Wait 方法的实现需要修订...?

private void buttonTest1_Click(object sender, EventArgs e)
{
    Thread thread = new(() =>
    {
        try
        {
            DoTest1Async().Wait();
            Debug.WriteLine("Unblocked!!");
        }
        catch (Exception exc)
        {
            MessageBox.Show(exc.ToString());
        }
    });
    thread.Start();
    thread.Join();
    Debug.WriteLine("Thread Finished!");
}
英文:

I guess this kind of things should not be done in general, but I would like to understand why this code is getting the UI blocked, not returning from the await taskMain:

using System.Diagnostics;

namespace TasksTest
{
    public partial class FormMain : Form
    {
        public FormMain()
        {
            InitializeComponent();
        }

        private void buttonTest1_Click(object sender, EventArgs e)
        {
            try
            {
                DoTest1Async().Wait();
            }
            catch (Exception exc) 
            {
                MessageBox.Show(exc.ToString());
            }
        }

        private static async Task DoTest1Async()
        {
            Task taskMain = Task.Run(() => {
                Debug.WriteLine("Test #1 completed");
            });
            await taskMain;
        }
    }
}

If instead of using Wait() I use await it works perfectly. I would like to know if there is a way of executing DoTest1Async synchronously without UI getting blocked.

------- EDITED ----------

Having understood that calling Wait method blocks the UI thread and doesn't allow the completed task taskMain to return to the UI thread context and resume execution... why this next code alternative does not block UI thread the same way??? I still don't understand why taskMain can return if I launch code in another thread having the UI thread blocked in the thread.Join but it can't return in a simple Wait() instruction. It sounds weird... Perhaps Wait method implementation needs a revision...?

        private void buttonTest1_Click(object sender, EventArgs e)
        {
            Thread thread = new(() =>
            {
                try
                {
                    DoTest1Async().Wait();
                    Debug.WriteLine("Unblocked!!");
                }
                catch (Exception exc)
                {
                    MessageBox.Show(exc.ToString());
                }
            });
            thread.Start();
            thread.Join();
            Debug.WriteLine("Thread Finished!");
        }

答案1

得分: 0

因为Task.Wait是一个阻塞操作:

Wait是一个同步方法,它使调用线程等待当前任务完成。如果当前任务尚未开始执行,Wait方法尝试从调度程序中移除任务,并在当前线程上内联执行它。如果无法执行此操作,或者当前任务已经开始执行,它将阻塞调用线程,直到任务完成。

它会返回,但是随后的Wait调用会阻塞线程。

如果使用await可以正常工作,那就使用它吧。async-await的引入正是为了在这种情况下使用。

我想知道是否有一种方式可以同步执行DoTest1Async而不会阻塞UI。

没有。根据定义。

此外,请阅读Stephen Cleary的《不要在异步代码上阻塞》。阻塞不仅会冻结UI线程,还可能导致死锁(似乎你遇到了一个)。

英文:

> but I would like to understand why this code is getting the UI blocked

Because Task.Wait is a blocking operation:

>> Wait is a synchronization method that causes the calling thread to wait until the current task has completed. If the current task has not started execution, the Wait method attempts to remove the task from the scheduler and execute it inline on the current thread. If it is unable to do that, or if the current task has already started execution, it blocks the calling thread until the task completes

> not returning from the await taskMain

It returns, but the subsequent Wait call blocks the thread.

> If instead of using Wait() I use await it works perfectly.

If it works - use it, one of the reason of async-await introduction is to use in cases exactly like this.

> I would like to know if there is a way of executing DoTest1Async synchronously without UI getting blocked.

No. By definition.

Also please read Don't Block on Async Code by Stephen Cleary. Blocking not only freezes UI thread but can lead to deadlocks (it seems that you are having one).

huangapple
  • 本文由 发表于 2023年8月9日 00:02:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76861330.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定