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

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

 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個關鍵字都是多線程機制的一部分,目的是讓處理用戶請求的線程儘快結束這次請求,結束後,就能夠用這個線程繼續接收其餘的請求。多線程

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

Task對象中封裝着線程相關的對象,async 和 await 都是爲Task服務。 想在函數中使用await,就必須聲明函數爲async的。這是由於這裏的await,理解爲掛起更爲恰當,當遇到await後,所在的函數執行必須掛起並當即返回主函數,而等待異步處理的結果得出後,繼續執行接下來的代碼。這樣的函數是特殊的,須要一個async來標識。框架

而這裏的Task<UserDto> 能夠理解爲,一個可以返回UserDto結果的,線程任務。Abp外層框架應該會對其進行await形式的調用,把結果返回給前臺。異步

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

 

第一段ide

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會對被它阻塞的代碼的運行線程產生影響!spa

 

第二段代碼:

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,通常都會和一個正在後臺線程相關。

這裏須要注意一點,先看代碼:

static Task<string> GetReturnResult()
        {
            Console.WriteLine("執行Task.Run以前, 線程ID:{0}", Thread.CurrentThread.ManagedThreadId);

            var task = new Task<string>(() => {
                Console.WriteLine("並行線程:GetReturnResult()方法裏面線程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("並行線程:running....");
                Thread.Sleep(3000);

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

            //用new建立的task對象,必須使用start方法開始任務,否則task不會開始,在這裏就會會永久阻塞程序。
            task.Start();

            return task;

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

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

雖然這個函數咱們僅僅須要Task<string>,可是其實一個task必需要start後,纔可能被啓動執行。而Task.Run 是自動調用start方法的。

這裏再帖一份代碼,是使用owin中間件進行重定向的代碼,其中有3種對task的使用方法:

    app.MapWhen((r) => !r.Request.Path.Value.ToLowerInvariant().StartsWith("/api")
          && !r.Request.Path.Value.ToLowerInvariant().StartsWith("/swagger")
          && !r.Request.Path.Value.ToLowerInvariant().Equals("/")
          && !r.Request.Path.Value.ToLowerInvariant().Equals("")
          ,
          spa =>
          {
              spa.Run(context =>
              {
                  //first
                  //context.Response.Redirect("/people");
           //雖然是同步邏輯,可是須要一個異步結構的返回值,使用Task.FromResult<>能夠構造
//return Task.FromResult<int>(0); //second //return Task.Run(() => context.Response.Redirect("/people")); //third var task = new Task(() => { context.Response.Redirect("/people"); }); //注意,必須手動啓動!!!!!!! task.Start(); return task; }); });
相關文章
相關標籤/搜索