您的位置:首页 > 大数据 > 人工智能

Async和await以及Task的爱恨情仇

2017-04-16 23:48 666 查看
参考文档:


异步方法的意义何在,Async和await以及Task的爱恨情仇,还有多线程那一家子。

任务Task

一个可以有返回值(需要等待)的多线程工具。

(Thread和ThreadPool接受的都是委托类型,所以可以单独定义方法在初始化的时候传入,接受的委托都返回void,所以都不能在线程里有返回值)

class Program
{
static void Main(string[] args)
{
//任务
Task.Run(() =>
{
Thread.Sleep(200);
Console.WriteLine("Task启动执行匿名方法");//这段代码(可能)第二被执行
});
Console.WriteLine("Task默认不阻塞"); //因为上面线程挂起了200毫秒+执行代码需要的实际时间,所以这段代码最先被执行

//获取Task.Result会造成阻塞等待task执行
int r = Task.Run(() =>
{
Console.WriteLine("Task启动执行匿名方法并返回值");//这段代码(可能)第三被执行
Thread.Sleep(1000);
return 5;
}).Result;  //这一步会阻塞线程

Console.WriteLine("返回值是:{0}", r); //因为上面代码调用了Result方法,而调用Result方法会阻塞线程,所以这段代码最后被执行

}
}






异步方法Async&await&Task

1 异步方法需要Async关键字修饰

2 异步方法的返回类型只能是void或Task<T>

3 返回值类型是T时,异步方法返回类型必须是Task<T>

4 await可以用于async方法和 async方法中的task(通过3、4两点大家应该能猜到,异步方法本身其实就是一个Task或者说和自己内部的Task在同一线程)

5 只有异步方法内使用了 (await关键词描述的)(有返回值的线程Task)才能提现异步方法的优势

class Program
{
//异步方法
public async Task<int> MethodA(DateTime bgtime, int i)
{
int r = await Task.Run(() =>
{
Console.WriteLine("异步方法{0}Task被执行", i);
Thread.Sleep(100);
return i * 2;
});
Console.WriteLine("异步方法{0}执行完毕,结果{1}", i, r);

if (i == 49)
{
Console.WriteLine("用时{0}", (DateTime.Now - bgtime).TotalMilliseconds);
}
return r;
}
//普通方法
public int MethodB(DateTime bgtime, int i)
{
int r = Task.Run(() =>
{
Console.WriteLine("普通多线程方法{0}Task被执行", i);
Thread.Sleep(100);
return i * 2;
}).Result;
Console.WriteLine("普通方法{0}执行完毕,结果{1}", i, r);

if (i == 49)
{
Console.WriteLine("用时{0}", (DateTime.Now - bgtime).TotalMilliseconds);
}
return r;
}

static void Main(string[] args)
{
Program p = new Program();

DateTime pbgtime = DateTime.Now;
for (int i = 0; i < 50; i++)
{
p.MethodB(pbgtime, i);
Console.WriteLine("普通方法{0}调用完成", i);
}

DateTime abgtime = DateTime.Now;
for (int i = 0; i < 50; i++)
{
p.MethodA(abgtime, i);
Console.WriteLine("异步方法{0}调用完成", i);
}

Console.ReadKey();
}
}


测试开始!------------------------------------------------------------------------------------------------

  第一次:都获取Task的返回结果,异步方法使用await获取,普通方法使用Task.Run().Result获取。测试结果:





可以发现普通方法由于阻塞执行都是按顺序执行,多线程失去意义。异步方法则并行执行,重要的是计算结果一样。所以在方法内需要使用Task结果时,异步方法使用await不阻塞调用进程优势明显。

第二次:异步方法中不使用await,使用和普通方法一样的Task.Run().Result获取结果。测试结果:





可以看到用时和执行顺序都一样。所以没有await的情况下,异步方法等待Task结果时一样会阻塞调用进程。

第三次:都只调用Task执行,不获取结果。测试结果:





可以看到,不管是普通方法还是异步方法都是多个线程并行执行,所以不获取结果的时,异步方法和普通多线程方法性能一样。

  在这次测试基础上,让异步方法await一个不返回结果的Task会发现,异步方法内还是会等待Task执行完毕。所以只要使用await不管是方法还是Task,有无返回结果,后面的代码都要等待其执行完毕。

重要的总结:

  【意义】异步方法的意义就是保证一个进程使用多线程多次执行一个方法时,不会因为其中某一次执行阻塞调用进程
  【原理】利用方法内Task调用新线程,await使方法内等待Task结果时调用进程不被阻塞,多次调用相当于多个线程并行。(不被阻塞的原因应该是异步方法本身就和内部的Task跑在一个线程里)
  【区别】普通方法只用Task也可以并行,当方法内需要Task返回值时,等待Task结果就会阻塞调用进程。
  【应用】主要应用在没有返回值,使用线程且需要线程返回结果的方法。

  一些分析:

  1.异步方法有返回值会怎样?

  因为异步方法返回类型是Task<T>,所以获取返回值只能await或者.Result,两者都会让当前方法等待。

  2.那么异步方法是不是没有作用了?

  如果是用.Result获取,那么是。如果是await就不一定了。await只能在async方法中使用,所以await获取异步方法返回值的方法也是异步的,再往上最终只能肯定是一个普通方法调用异步方法。是否有用取决于普通方法内调用最上层异步方法的方式。

  3.为什么返回值类型是T,方法返回类型需要是Task<T>?

  要达到异步方法内等待线程结果不阻塞调用进程,这个方法本身就应该在线程中执行。所以不管返回类型是什么,放到Task中运行后返回的是Task<T>。这样被调用时相当于一个Task.Run(),也就可以实现异步方法await了。

  4.为什么要实现异步方法await可等待?

  异步方法的await其实第二点已经分析了,实现异步方法await可以允许异步方法内继续调用异步方法,把异步操作从底层向上层传递。而能够传递到的最上层是什么,是static void Main(),所以最终还是普通方法调用异步方法。也就是说不能继续使用await等待异步方法的结果了,当最上层不关注返回结果时,不管内部有多少次await异步方法的调用,依然还是多线程的并行。如果最上层非要关注异步方法的返回结果,用.Result获取其结果,那我无话可说。

  5.关于Async和await。

  await其实不光是一个简单的让下一行代码等待异步方法或Task结果的关键字。应该理解成一个扩大当前Task代码执行范围的命令。

  从最开始的await Task让整个异步方法B都能在Task中运行(所以普通方法调用异步方法B时,B内await Task结果就不会阻塞调用进程)。

  到异步方法A中await异步方法B让异步方法A和B都在同一Task内运行(所以普通方法调用异步方法A时,A内await异步方法B的结果和B内await Task的结果就不会阻塞调用进程)

  Async用于标识一个方法是异步方法,约束其返回类型为Task<T>。也就说内部可以使用await,且方法本身是放到Task中执行的,所以代码返回类型T,方法的返回类型却是Task<T>。

 

  最后一定要区别异步方法和普通多线程方法的用处,他们的关键区别就是是否需要单独等待线程的执行结果。不要把异步方法当多线程方法用了。

测试

namespace ConsoleTest
{

class Program
{
static void Main(string[] args)
{
Thread T = new Thread(R => Console.WriteLine(1));
T.Start();

Console.WriteLine("主线程启动");

CallFuncAsync().ContinueWith(r=>Console.Write("异步调用完成!异步的结果是"+r.Result));

Console.WriteLine("主线程执行完毕");
Console.ReadKey();
}

public static async Task<int> CallFuncAsync()
{

Console.WriteLine("异步方法启动");

return await CallFunc();

}

public static Task<int> CallFunc()
{
return Task.Run(() => { Console.WriteLine("同步方法开始"); Task.Delay(2000); return 3; });
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: