为什么我对CountCharsAsync的调用会阻止UI?

时间:2018-06-06 15:59:03

标签: c#

我想我在这里遗漏了一些东西.. 我期望对CountCharsAsync()的调用是非阻塞的。但它似乎并非如此。当我点击我的按钮时,前五秒没有任何事情发生..并且UI被卡住......

但第二个版本没问题。

有人可以解释这两种方式之间的区别......

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AsyncAwaitExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text += "Count is : ";
            int count = await CountCharsAsync();
            textBox1.Text += count;

            textBox1.Text += "\r\n-----------\r\n";

            textBox1.Text += "Count is : ";
            Task<int> tsk = new Task<int>(CountChars);
            tsk.Start();
            int count2 = await tsk;
            textBox1.Text += count2;

        }

        private int CountChars()
        {
            int count = 50;
            Thread.Sleep(5000);
            return count;
        }

        private async Task<int> CountCharsAsync()
        {
            int count = 50;
            Thread.Sleep(5000);
            return count;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

您正在异步方法中调用Thread.Sleep。这将阻止它正在运行的任何线程......这仍然是UI线程。调用异步方法本身不会启动新线程。可能涉及其他线程,具体取决于方法中发生的具体情况,但在第一个await表达式之前,该方法将在与调用代码相同的线程上同步执行...并且您的异步方法没有任何线程等待表达式(应该已经给你一个警告)。

您应该使用:

await Task.Delay(5000);

代替。这是一种“异步等待”。这样,UI线程将不会被阻止。 CountCharsAsync返回的任务将在约5秒后完成,此时button1_Click的下一部分将会执行。

将此与此代码进行比较:

Task<int> tsk = new Task<int>(CountChars);
tsk.Start();

这将在线程池上的默认任务调度程序中执行CountChars。阻止 线程根本不会影响UI。 (正如评论中所指出的那样,现在这不是启动任务的好方法。要么调用异步方法,要么使用Task.Run。)

答案 1 :(得分:0)

虽然Daisy的回答使它不能阻止UI,但你的CountCharsAsync方法是“假的”异步。如果您有Resharper(不确定这是VS或R#的功能),您将收到警告:

  

异步方法缺少'await'运算符并将同步运行

这是因为异步任务没有完成所有的魔法 - 在你的情况下,它绝对没有任何意义。无处使你在另一个线程上运行。

正确实施将是:

private int CountChars()
        {
            int count = 50;
            Thread.Sleep(5000);
            return count;
        }

        private Task<int> CountCharsAsync() // notice no async!
        {
            return Task.Run(CountChars);
        }

如果你用一个for循环替换Thread.Sleep到最大整数,Daisy的实现仍会阻塞。

我的版本实际上将执行转移到新线程。