.Net中async、await的用法及其与Task的关系

本文中使用的示例代码都是在WinForm中编写调试的,我根据是否使用await处理Task、调用异步方法/非异步方法、方法的返回值为Task/Task<Student>,做了8种情况的分析和总结。

public class CommonUtil
{
    public static async Task<Student> AsyncGetStudentInfoHasReturn(string sname)
    {
        string name = $"Handle:{sname}";
        var result = await Task.Run<Student>(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Sub01-Start【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            Thread.Sleep(4000);
            Console.WriteLine("Sub01-End【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            return new Student() { Id = "01", SName = sname, Age = 12 };
        });
        Console.WriteLine("Sub01-await之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        result = await Task.Run<Student>(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Sub02-Start【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            Thread.Sleep(4000);
            Console.WriteLine("Sub02-End【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            return new Student() { Id = "01", SName = sname, Age = 12 };
        });
        Console.WriteLine("Sub02-await之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        return result;
    }

    public static async Task AsyncGetStudentInfoNoReturn(string sname)
    {
        string name = $"Handle:{sname}";
        await Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Sub01-Start【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            Thread.Sleep(4000);
            Console.WriteLine("Sub01-End【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        });
        Console.WriteLine("Sub01-await之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        await Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Sub02-Start【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            Thread.Sleep(4000);
            Console.WriteLine("Sub02-End【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        });
        Console.WriteLine("Sub02-await之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    }

    public static Task<Student> GetStudentInfoHasReturn(string sname)
    {
        string name = $"Handle:{sname}";
        Task<Student> task = Task.Run<Student>(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Sub-Start【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            Thread.Sleep(4000);
            Console.WriteLine("Sub-End【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            return new Student() { Id = "01", SName = sname, Age = 12 };
        });
        Console.WriteLine("Sub-TaskRun之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        return task;
    }

    public static Task GetStudentInfoNoReturn(string sname)
    {
        string name = $"Handle:{sname}";
        Task task = Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("Sub-Start【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
            Thread.Sleep(4000);
            Console.WriteLine("Sub-End【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        });
        Console.WriteLine("Sub-TaskRun之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        return task;
    }
}

一、使用await调用返回值为Task<Student>的异步方法

/// <summary>
/// 使用await调用返回值为Task<Student>的异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnInvokeAsyncHasResultUseAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    var task01 = CommonUtil.AsyncGetStudentInfoHasReturn("AAA");
    Thread.Sleep(2000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    await task01;//await时界面不卡顿
    Console.WriteLine("【3】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Student student01 = task01.Result;//await之后获取返回值时无需再等待
    Console.WriteLine("【4】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

二、不使用await调用返回值为Task<Student>的异步方法

/// <summary>
/// 不使用await调用返回值为Task<Student>的异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnInvokeAsyncHasResultNoAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    var task01 = CommonUtil.AsyncGetStudentInfoHasReturn("AAA");
    Thread.Sleep(2000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Student student01 = task01.Result;//调用有返回值的异步方法时,省略await直接用task01.Result获取返回值时会一直等待
    Console.WriteLine("【3】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

三、使用await调用返回值为Task的异步方法

/// <summary>
/// 使用await调用返回值为Task的异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnInvokeAsyncNoResultUseAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    var task01 = CommonUtil.AsyncGetStudentInfoNoReturn("AAA");
    Thread.Sleep(20000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    await task01;//await时界面不卡顿,必须等CommonUtil.AsyncGetStudentInfoNoReturn("AAA")执行完最后一行代码才算await执行完毕
    Console.WriteLine("【3】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

四、不使用await调用返回值为Task的异步方法

/// <summary>
/// 不使用await调用返回值为Task的异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnInvokeAsyncNoResultNoAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    var task01 = CommonUtil.AsyncGetStudentInfoNoReturn("AAA");//执行时界面不卡顿
    Thread.Sleep(20000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

 

五、使用await调用返回值为Task<Student>的方法 

/// <summary>
/// 使用await调用返回值为Task<Student>的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnInvokeTaskHasResultUseAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Task<Student> task = CommonUtil.GetStudentInfoHasReturn("AAA");
    Thread.Sleep(2000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    await task;//await时界面不卡顿
    Console.WriteLine("【3】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Student student02 = task.Result;//await之后获取返回值时无需再等待
    Console.WriteLine("【4】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

 

六、不使用await调用返回值为Task<Student>的方法  

/// <summary>
/// 不使用await调用返回值为Task<Student>的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnInvokeTaskHasResultNoAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Task<Student> task = CommonUtil.GetStudentInfoHasReturn("AAA");
    Thread.Sleep(2000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Student student01 = task.Result;//通过task.Result获取返回值时界面会卡顿
    //Student student02 = task.GetAwaiter().GetResult();//通过task.GetAwaiter().GetResult()获取返回值时界面会卡顿
    Console.WriteLine("【3】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

 七、使用await调用返回值为Task的方法 

/// <summary>
/// 使用await调用返回值为Task的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnInvokeTaskNoResultUseAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Task task = CommonUtil.GetStudentInfoNoReturn("AAA");
    Thread.Sleep(2000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    await task;//await时界面不卡顿,必须等CommonUtil.GetStudentInfoNoReturn("AAA")执行完最后一行代码才算await执行完毕
    Console.WriteLine("【3】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

八、不使用await调用返回值为Task的方法

/// <summary>
/// 不使用await调用返回值为Task的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnInvokeTaskNoResultNoAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    var task01 = CommonUtil.GetStudentInfoNoReturn("AAA");//执行时界面不卡顿
    Thread.Sleep(2000);
    Console.WriteLine("【2】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
}

 

九、总结 

1.1、async方法中可以调用有返回值的async方法、没返回值的async方法、有返回值的非async方法、没返回值的非async方法

1.2、非async方法中可以调用没返回值的async方法、有返回值的非async方法、没返回值的非async方法

1.3、非async方法中不能调用有返回值的async方法,因为有返回值的async方法必须使用await获取返回值,而非async方法中不能使用await

2.1、async方法有返回值时必须使用await让主线程等待任务执行完毕并获取返回值

2.2、async方法没返回值时可以使用await让主线程等待任务执行完毕,也可以不使用await在主线程空闲时再等待任务执行完毕

2.3、主线程什么时候是空闲呢?

3.1、使用await获取async方法、非async方法的返回值时界面不卡顿

3.2、使用task.Result获取非async方法的返回值时界面会卡顿

3.3、使用task.Result无法获取async方法的返回值,必须使用await获取async方法的返回值

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

changuncle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值