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

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

public class CommonUtil
{
    public static async Task AsyncGetStudentInfoHasReturn(string sname)
    {
        string name = $"Handle:{sname}";
        var result = 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);
            return new Student() { Id = "01", SName = sname, Age = 12 };
        });
        Console.WriteLine("Sub01-await之后【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
        result = 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);
            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 GetStudentInfoHasReturn(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);
            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的异步方法

/// 
/// 使用await调用返回值为Task的异步方法
/// 
/// 
/// 
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);
}

.Net中async、await的用法及其与Task的关系_第1张图片

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

/// 
/// 不使用await调用返回值为Task的异步方法
/// 
/// 
/// 
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);
}

.Net中async、await的用法及其与Task的关系_第2张图片

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

/// 
/// 使用await调用返回值为Task的异步方法
/// 
/// 
/// 
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);
}

.Net中async、await的用法及其与Task的关系_第3张图片

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

/// 
/// 不使用await调用返回值为Task的异步方法
/// 
/// 
/// 
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);
}

 .Net中async、await的用法及其与Task的关系_第4张图片

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

/// 
/// 使用await调用返回值为Task的方法
/// 
/// 
/// 
private async void btnInvokeTaskHasResultUseAwait_Click(object sender, EventArgs e)
{
    Console.WriteLine("【1】Main【ThreadId=" + Thread.CurrentThread.ManagedThreadId + "】:" + DateTime.Now);
    Task 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);
}

 .Net中async、await的用法及其与Task的关系_第5张图片

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

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

.Net中async、await的用法及其与Task的关系_第6张图片

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

/// 
/// 使用await调用返回值为Task的方法
/// 
/// 
/// 
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);
}

.Net中async、await的用法及其与Task的关系_第7张图片

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

/// 
/// 不使用await调用返回值为Task的方法
/// 
/// 
/// 
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);
}

 .Net中async、await的用法及其与Task的关系_第8张图片

九、总结 

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方法的返回值

你可能感兴趣的:(C#基础)