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; }); });
相关文章
相关标签/搜索