C#学习教程:什么时候最好使用Task.Result而不是等待Task,但我最近才开始研究它并了解发生了什么。我刚刚检查了我的代码并尝试更改它,所以如果一个任务可以与一些工作并行完成,那么它就是。例如:varuser=await_userRepo.GetByUsername(User.Identity.Name);//一些不依赖用户对象的小工作user=await_userRepo.UpdateLastAccessed(user,DateTime.Now);返回用户;现在变成:varuserTask=_userRepo.GetByUsername(User.Identity.Name);//一些不依赖用户对象的工作user=await_userRepo.UpdateLastAccessed(userTask.Result,DateTime.Now);返回用户;据了解,一些不相关的工作正在进行,同时从数据库中获取用户对象。但是,我看到的帖子表明结果应该很少使用,并且等待是首选但我不明白为什么我要等待我的用户对象被获取,如果我可以执行一些其他独立的逻辑同一时间?我们一定不能把这个地方埋在这里:所以例如:[一些正确的代码]变成了[一些不正确的代码]绝不能这样做。您可以重新调整控制流以提高性能的直觉是非常好的和正确的。使用Result这样做是错误的。重写代码的正确方法是varuserTask=_userRepo.GetByUsername(User.Identity.Name);//一些不依赖用户对象的工作user=await_userRepo.UpdateLastAccessed(awaituserTask,DateTime.Now);返回用户;请记住,await不会使调用异步。等待只是意味着“如果此任务的结果尚不可用,请执行其他操作并在可用时返回此处”。该调用已经是异步的:它返回一个任务。人们似乎认为await具有普通调用的语义;它没有。相反,await是对任务comonad的获取操作;它是任务的操作符,而不是调用表达式。您通常只会在方法调用中看到它,因为它是将异步操作抽象为方法的常见模式。返回的Task是要await的东西,而不是call。然而,我看到的帖子建议结果应该很少使用,等待是首选,但我不明白为什么我要等待我的用户对象被获取,如果我可以执行一些其他独立的逻辑同时?为什么你认为使用Result会让你同时执行其他独立的逻辑?结果完全阻止你这样做。结果是同步等待。您的线程在同步等待任务完成时无法执行任何其他工作。使用异步等待提高效率。请记住,await只是意味着“此工作流无法继续,直到此任务完成,因此如果它未完成,请找到更多工作要做,稍后再回来”。正如您所指出的,过早等待会导致效率低下,因为即使任务未完成,工作流有时也会继续进行。无论如何,移动等待发生的地方可以使您的工作流程更有效率,但永远不要将它们更改为结果。如果您认为使用Result会不断提高工作流中并行的效率,那么您对异步工作流的工作方式有一些深刻的误解。检查你的信念,看看你是否能弄清楚是哪个信念给了你这种不正确的直觉。您绝不能像这样使用Result的原因不仅仅是因为同步等待在执行异步工作流时效率低下。它最终会挂起你的进程。考虑以下工作流:现在假设Foo替换了task1的结果。怎么了?Foo同步等待task1完成,等待当前线程可用,这永远不会发生,因为我们处于同步等待中。如果任务以某种方式与当前线程相关联,则调用Result可能会使线程本身死锁。您现在可以在没有锁的情况下制造死锁,只有一个线程!不要这样做。在您的情况下,您可以使用:user=await_userRepo.UpdateLastAccessed(awaituserTask,DateTime.Now);或者更清楚:varuser=await_userRepo.GetByUsername(User.Identity.Name);//一些不依赖用户对象的工作user=await_userRepo.UpdateLastAccessed(user,DateTime.Now);唯一一次你应该触摸它。.Result是您知道任务已完成的地方。这在某些情况下很有用,您试图避免创建异步状态机,并且您认为任务可能会同步完成(可能对异步情况使用本机函数),或者您正在使用回调而不是异步/await,你在中间。作为避免状态机的示例:ValueTaskFetchAndProcess(SomeArgsargs){vartask=GetAsyncData(args);如果(!task.IsCompletedSuccessfully)返回等待(任务);返回新的ValueTask(SomeOtherProcessing(task.Result));这里的重点是,如果GetAsyncData返回一个同步完成的结果,我们就完全避免了所有的异步机制。异步等待并不意味着多个线程将运行您的代码。但是,它减少了线程等待进程完成的时间,从而提前完成。每当一个线程通常必须等待某事完成时,比如等待网页下载、数据库查询完成、磁盘写入完成,异步等待线程不会等到数据被写入/获取,但是环顾四周看看它是否可以做其他事情,并在等待任务完成后再回来。在EricLippert的这篇概述中,这被描述为厨师类比。在中间某处搜索asyncawait。EricLippert将async-await比作必须做早餐的厨师。他开始烤面包后,他可以闲着等面包烤好,然后开水壶泡茶,等水烧开,再把茶叶放进茶壶等等。一个异步等待的厨师,会不等敬酒,而是端上水壶,等水烧热了,他就把茶倒进茶壶里。每当厨师不得不无所事事地等待某事时,他就会环顾四周,看看是否还有其他事情可以做。异步函数中的线程会做类似的事情。因为函数是异步的,所以你知道函数中有一个等待点。事实上,如果你忘记编写await,编译器会警告你。当你的线程遇到await时,它会在调用堆栈中上升,看看它是否可以做其他事情,直到它看到await,再次调用堆栈,等等。一旦每个人都在等待,他调用堆栈并开始等待,直到第一个等待过程结束。等待的过程完成后,线程会继续处理等待之后的语句,直到他再次看到等待。可能是另一个线程将在await之后继续处理该语句(您可以通过检查线程ID在调试器中看到这一点)。但是这个其他线程具有原始线程的上下文,因此它可以像原始线程一样运行。不需要互斥量、信号量、IsInvokeRequired(在winforms中)等。对你来说,似乎有一个线程。有时,您的厨师必须做一些需要花费一些时间且不能闲着的事情,例如切西红柿。在这种情况下,聘请不同的厨师并命令他做切片可能是明智的。同时,您的厨师可以继续煮沸并要求去皮鸡蛋。用计算机术语来说,如果您在不等待其他进程的情况下进行一些繁重的计算,那就不同于将数据写入磁盘。一旦您的线程发出需要将数据写入磁盘的命令,它通常会等待数据写入。进行大型计算时情况并非如此。您可以使用Task.Run雇佣外的厨师asyncTaskCalculateSunSet(){//开始获取日落数据。但是不要等待结果//你有更好的事情要做:TasktaskFetchData=FetchSunsetData();//因为你没有等待你的线程将执行以下操作:Locationlocation=FetchLocation();//现在你需要日落数据,开始等待任务:SunsetDatasunsetData=awaittaskFetchData;//需要一些大的计算,需要33秒,//你想让你的调用者保持响应,所以启动一个任务//这个任务将由不同的线程运行:asktaskBigCalculations=Taks.Run(()=>BigCalculations(sunsetData,location);//再次没有等待:你仍然可以自由地做其他事情......//在返回之前你需要大计算的结果。//等到大计算完成,保持调用者响应:DateTimeresult=awaittaskBigCalculations;returnresult;}你想过这个版本吗?varuserTask=_userRepo.GetByUsername(User.Identity.Name);//一些不依赖用户对象的工作user=await_userRepo.UpdateLastAccessed(awaituserTask,DateTime.Now);返回用户;这将在检索用户时完成“工作”,但它也具有在完成任务的任务中等待任务的所有优点,如task.Result?建议大家也可以使用更显式的版本在调试器中检查调用的结果很有用,需要了解更多C#学习教程,希望大家多多关注—varuserTask=_userRepo.GetByUsername(User.Identity。姓名);//一些不依赖用户对象的工作user=awaituserTask;user=await_userRepo.UpdateLastAccessed(user,DateTime.Now);返回用户;本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
