1. 程式人生 > >ASP.NET 異步返回的Action (編輯中。。。)

ASP.NET 異步返回的Action (編輯中。。。)

err 接收 直接 ogr ret 返回 上層 不理解 wait

新學.net,最近用到了ABP框架,發現了如下代碼:

 public override async Task<UserDto> Get(EntityDto<long> input)
        {
            var user = await base.Get(input);
            var userRoles = await _userManager.GetRolesAsync(user.Id);
            user.Roles = userRoles.Select(ur => ur).ToArray();
            
return user; }

看到後,對async , Task , await 完全不理解,就查閱了相關資料。

簡單說一下我的理解,這3個關鍵字都是多線程機制的一部分,目的是讓處理用戶請求的線程盡快結束此次請求,結束後,就可以用這個線程繼續接收其他的請求。

而費時的異步操作,交給後臺線程處理(這裏的後臺線程,應該不能響應用戶請求)。這樣一來,就能讓服務器更快地響應請求。

Task對象中封裝著線程相關的對象,async 和 await 都是為Task服務。 想在函數中使用await,就必須聲明函數為async的。

關於await 和 async 的理解,我貼2段代碼,這2段代碼是在搜資料時,從網上找到的,我對其進行了改進,感謝原作者的付出!

第一段

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main start-----Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);

            Test(); 
Console.WriteLine("-----Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("-----Main end:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); } static async Task Test() { Console.WriteLine("======Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); // 方法打上async關鍵字,就可以用await調用同樣打上async的方法 await GetName(); Console.WriteLine("Middle---------Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); await GetName2(); Console.WriteLine("##########Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); } static async Task GetName() { Console.WriteLine("*****Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); // Delay 方法來自於.net 4.5 // await Task.Delay(3000); // 返回值前面加 async 之後,方法裏面就可以用await了 await Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("^^^^^^^^Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("$$$$$$$Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); } static async Task GetName2() { Console.WriteLine("!!!!!Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("++++++++Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("~~~~~~Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); } } }

輸出如下:

Main start-----Current Thread Id :1
======Current Thread Id :1
*****Current Thread Id :1
-----Current Thread Id :1
-----Main end:1
^^^^^^^^Current Thread Id :3
$$$$$$$Current Thread Id :3
Middle---------Current Thread Id :3
!!!!!Current Thread Id :3
++++++++Current Thread Id :4
~~~~~~Current Thread Id :4
##########Current Thread Id :4

上面這段代碼,可以明顯地看 代碼運行的線程,await會對被它阻塞的代碼的運行線程產生影響!

第二段代碼:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
    
        static void Main(string[] args)
        {
            Console.WriteLine("我是主線程,線程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.WriteLine("-----------------TestAsync 返回--------------------------");
            Console.ReadLine();
        }


        //為了在函數體內使用await,必須寫async,表示這是個異步函數。
        //而這裏使用 await 的作用,就是能讓 TestAsync 函數立即返回main函數,去執行main之後的邏輯,而不用等待 await 的task.在await的task執行完畢後,繼續執行當前task。
        //對於一個async函數,可以使用await 去等待結果返回,也可以不使用,就像main函數這樣,實現真正的異步效果,直接運行到了函數結尾。
        static async Task TestAsync()
        {
            Console.WriteLine("調用GetReturnResult()之前,線程ID:{0}。當前時間:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //創建task,但沒有等待
            var name = GetReturnResult();
            Console.WriteLine("調用GetReturnResult()之後,線程ID:{0}。當前時間:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));


            //會阻塞上層調用的寫法
            //Console.WriteLine("得到GetReturnResult()方法的結果:{0}。當前時間:{1}", name.Result, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            //不會阻塞上層調用的寫法
            //這個await 找到最終的那個 新線程await,並 跳過等待結果,直接返回
            Console.WriteLine("得到GetReturnResult()方法的結果:{0}。當前時間:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("await 後面的邏輯 => TestAsync 最後一句");
        }

        static Task<string> GetReturnResult()
        {
            Console.WriteLine("執行Task.Run之前, 線程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            return Task.Run(() =>
            {
                Console.WriteLine("並行線程:GetReturnResult()方法裏面線程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("並行線程:running....");
                Thread.Sleep(3000);

                return "我是返回值";
            });

        }

    }
}

結果如下:

我是主線程,線程ID:1
調用GetReturnResult()之前,線程ID:1。當前時間:2018-06-28 03:06:32
執行Task.Run之前, 線程ID:1
調用GetReturnResult()之後,線程ID:1。當前時間:2018-06-28 03:06:32
-----------------TestAsync 返回--------------------------
並行線程:GetReturnResult()方法裏面線程ID: 3
並行線程:running....
得到GetReturnResult()方法的結果:我是返回值。當前時間:2018-06-28 03:06:35
await 後面的邏輯 => TestAsync 最後一句

上面這段代碼,可以看出async 和await 的具體效果: 主函數直接調用一個用async聲明的函數(不在前面加await),運行到async聲明的函數中的await後立即返回主函數,不會阻塞主函數的代碼塊。這個函數返回的task,一般都會和一個正在後臺線程相關。

ASP.NET 異步返回的Action (編輯中。。。)