繁体   English   中英

异步等待性能-直接方法调用与任务包装器调用

[英]Async Await performance - Direct method call vs Task wrapper call

我创建了一个小程序来理解Async Await的工作,并在for循环中将async方法作为直接方法调用来调用:

sumProgram.CallSum(i, i + 1);

或使用任务API Task.Run / Task.Factory.StartNew

我最初的理解是Task API会大大提高它的速度,但是与我的预期相反,这肯定反映出我的理解不足,直接调用在性能方面要好得多。 实际上,当在GetSum方法中引入了额外的线程睡眠时,它似乎仅影响Task调用,而且影响很大。

现在,我知道直接调用的第一部分更快,因为它们是异步执行的,并且没有添加任务列表并使它们等待的开销,但是当我将相同的技术转移到实际的编程范例时,然后提出疑问遗迹:

  • 对于直接调用,没有什么可模拟Task.WaitAll,因此即使所有执行未完成,它们也将退出调用方法,所以我唯一的选择是Task包装器。

  • 我是否得到令人困惑的结果,因为对于直接调用,秒表将在所有执行完成之前发布时间,而由于waitAll,任务包装器不会出现这种情况

  • 为了执行此程序,您需要注释/取消注释相关部分以获取正确的结果

      class Program { static void Main(string[] args) { Program sumProgram = new Program(); List<Task> taskList = new List<Task>(); // Only For Task Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 100000; i++) { taskList.Add(Task.Factory.StartNew(() => { sumProgram.CallSum(i, i + 1); })); // For Task use one taskList.Add(Task.Run(() => { sumProgram.CallSum(i, i + 1); })); // For Task use one sumProgram.CallSum(i, i + 1); } Task.WaitAll(taskList.ToArray()); // Only For Task sw.Stop(); Console.WriteLine("Time Elapsed :: " + sw.ElapsedMilliseconds); } public async Task CallSum(int num1, int num2) { Func<int> callFunc = (() => { return GetSum(num1, num2); }); int result = await Task.Run<int>(callFunc); //Console.WriteLine("Sum Result :: " + result); } public int GetSum(int num1, int num2) { Thread.Sleep(10); return (num1 + num2); } } 

async !=“更快”

实际上, async代码几乎总是(稍微)慢一些。

那么,为什么要使用async

在客户端(UI)应用程序上, async允许您保持对用户的响应。 对比这段代码:

void Button_Click()
{
  Thread.Sleep(10000);
}

使用此代码:

async void Button_Click()
{
  await Task.Delay(10000);
}

在服务器端(例如ASP.NET), async允许您的代码使用更少的线程来服务更多的请求。 因此,增加了可伸缩性。

请注意,在这两种情况下, async代码实际上都比同步代码 对于UI,休眠当前线程要比创建和启动计时器,创建并等待任务,然后在计时器触发时恢复异步方法快。 对于ASP.NET,阻塞当前线程比创建异步操作任务要快,然后在异步方法恢复时将请求上下文切换到另一个线程要快得多。

但是, async带来了其他好处。 对于UI,在延迟期间不会阻塞UI线程。 对于ASP.NET,在进行异步操作时,请求线程可以自由处理其他请求。

async与速度无关; 这是关于释放当前线程。

上面的评论说,您在Task内完成的工作太少,以致于无法超过设置成本。

另外,Task和async-await并不适用于直接调用尽可能容易的情况。 使某些东西异步并不能使其更快。 它们适用于您使调用线程保持运行/做其他事情,同时等待一个漫长的/失败的/可取消的任务,以使硬件能够真正并行化的方式在内核之间完成或分配足够的工作的情况。 而且,无需使用表征异步编程的难看的,由内而外的代码即可完成所有这些工作。

很简单,您的实验是错误的。 您所测量的是在无法成功的情况下在.NET中使用Task机制的过失。

试试这个吧。 在每个任务中,将一些内存从一个位置复制到另一个位置(使用Marshal.AllocHGlobal进行分配,并使用P / Invoke CopyMemory复制)。 达到临界阈值后,您将看到此机制与单线程副本相比要快多少。 另外,使用async-await会使您的代码看起来更好。

您的代码的最大问题是您正在使用的选项节点正在等待CallSum()完成。 为此,请使用CallSum()返回的Task并等待它。 例如:

taskList.Add(sumProgram.CallSum(i, i + 1));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM