C# 异步等待性能-直接方法调用与任务包装器调用
我创建的一个小程序,用于理解Async Wait的工作原理,并在for循环中调用Async方法,作为直接方法调用:C# 异步等待性能-直接方法调用与任务包装器调用,c#,.net,asynchronous,task-parallel-library,task,C#,.net,Asynchronous,Task Parallel Library,Task,我创建的一个小程序,用于理解Async Wait的工作原理,并在for循环中调用Async方法,作为直接方法调用: sumProgram.CallSum(i, i + 1); 或者使用任务APITask.Run/Task.Factory.StartNew 我最初的理解是任务API将使其速度大大加快,但与我的预期相反,直接调用在性能方面要好得多,这肯定反映了我的理解不足。事实上,当在GetSum方法中引入额外的线程睡眠时,它似乎只影响任务调用,而且影响也很大 现在我理解了直接调用的第一部分更快,
sumProgram.CallSum(i, i + 1);
或者使用任务APITask.Run/Task.Factory.StartNew
我最初的理解是任务API将使其速度大大加快,但与我的预期相反,直接调用在性能方面要好得多,这肯定反映了我的理解不足。事实上,当在GetSum方法中引入额外的线程睡眠时,它似乎只影响任务调用,而且影响也很大
现在我理解了直接调用的第一部分更快,因为它们是异步执行的,并且没有添加到任务列表并让它们等待的开销,但是当我将相同的技术转移到实际编程范例时,问题仍然存在:
- 对于直接调用,没有任何东西可以模拟Task.WaitAll,因此它们将退出调用方法,即使所有的执行都没有完成,我唯一的选项任务包装器也是如此
- 我是否得到了令人费解的结果,因为对于直接调用,秒表甚至在所有执行完成之前都会发布时间,而对于任务包装器来说,由于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); } }
类程序 { 静态void Main(字符串[]参数) { 程序sumProgram=新程序(); List taskList=new List();//仅适用于任务 秒表sw=Stopwatch.StartNew(); 对于(int i=0;i<100000;i++) { taskList.Add(Task.Factory.StartNew(()=>{sumProgram.CallSum(i,i+1);}));//对于任务,使用一个 taskList.Add(Task.Run(()=>{sumProgram.CallSum(i,i+1);}));//对于任务使用一个 sumProgram.CallSum(i,i+1); } Task.WaitAll(taskList.ToArray());//仅用于任务 sw.Stop(); Console.WriteLine(“经过的时间::”+sw.ElapsedMilliseconds); } 公共异步任务调用和(int num1,int num2) { Func callFunc=(()=> { 返回GetSum(num1,num2); }); int result=wait Task.Run(callFunc); //Console.WriteLine(“总和结果::”+结果); } 公共整数GetSum(整数m1,整数m2) { 睡眠(10); 返回值(num1+num2); } }
- 上面的评论是说,您在任务内部完成的工作太少,无法超过它的安装成本
此外,任务和异步等待并不适用于直接调用尽可能容易的场景。使某些东西异步并不能使它更快。它们适用于这样的场景:在等待一个长时间的/可失败的/可取消的任务完成或以硬件实际并行化的方式跨内核分配足够的工作时,让调用线程继续运行/执行其他操作。并且在不使用丑陋的、由内而外的异步编程代码的情况下完成所有这些工作
很简单,你的实验是错误的。您所测量的是无意中听到的在.NET中使用任务机制的情况,在这种情况下,它不可能获胜
试试这个。在每个任务中,将一些内存从一个位置复制到另一个位置(使用Marshal.AllocHGlobal分配,并使用P/Invoke CopyMemory复制)。一旦达到临界阈值,您将看到此机制比单线程拷贝快多少。另外,使用async Wait您的代码看起来会更好。上面的评论是,您在任务内部完成的工作太少,无法超过它的安装成本 此外,任务和异步等待并不适用于直接调用尽可能容易的场景。使某些东西异步并不能使它更快。它们适用于这样的场景:在等待一个长时间的/可失败的/可取消的任务完成或以硬件实际并行化的方式跨内核分配足够的工作时,让调用线程继续运行/执行其他操作。并且在不使用丑陋的、由内而外的异步编程代码的情况下完成所有这些工作 很简单,你的实验是错误的。您所测量的是无意中听到的在.NET中使用任务机制的情况,在这种情况下,它不可能获胜
试试这个。在每个任务中,将一些内存从一个位置复制到另一个位置(使用Marshal.AllocHGlobal分配,并使用P/Invoke CopyMemory复制)。一旦达到临界阈值,您将看到此机制比单线程拷贝快多少。另外,使用async Wait您的代码看起来会更好。代码的最大问题是您使用的选项节点正在等待
CallSum()
完成。为此,获取CallSum()
返回的Task
,然后等待它。例如:
taskList.Add(sumProgram.CallSum(i, i + 1));
代码的最大问题是,您正在使用的选项节点正在等待
CallSum()
完成。为此,获取CallSum()
返回的Task
,然后等待它。例如:
taskList.Add(sumProgram.CallSum(i, i + 1));
异步
!=“更快”
实际上,async
代码几乎总是(稍微)慢一些
那么,为什么要使用async
在客户端(UI)应用程序中,async
允许您对用户保持响应。对比此代码:
void Button_Click()
{
Thread.Sleep(10000);
}
async void Button_Click()
{
await Task.Delay(10000);
}
使用此代码:
void Button_Click()
{
Thread.Sleep(10000);
}
async void Button_Click()
{
await Task.Delay(10000);
}
在服务器端(例如ASP.NET),async
允许您的代码使用更少的线程来处理更多的请求。因此,增加了可伸缩性
请注意,在这两种情况下,async
代码实际上比同步代码慢。对于UI,休眠当前线程比创建和启动计时器、创建和等待要快