本文中使用的示例代碼都是在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方法的傳回值