在描述实际问题之前,需要更多信息来提供代码.
起初,代码中没有异步操作.我只是使用默认任务调度程序将大约500,000个任务安排到Threadpool中,每个任务都将通过例程,阻止所有EF(实体框架)查询并顺序完成其工作.这很好,但还不够快.然后我将所有EF查询更改为Async,结果是速度极佳,但sql服务器中存在如此多的死锁和超时,大约三分之一的订阅者从未收到过文本!在尝试不同的解决方案之后,我决定在24核服务器上运行超过500,000个任务(至少有24个并发线程池线程)时不要执行太多的异步数据库操作!
我回滚了所有更改(Asycn)期望在每个仍然异步的任务中进行一次Web服务调用.
现在奇怪的情况:
在我的代码中,我有一个名为“isCrossSellActive”的布尔变量.设置变量时,会发生更多数据库操作,并且会发生线程等待的asycn webservice调用.当此变量为false时,将不会发生这些操作,包括异步Web服务调用.设置变量时,代码运行速度比没有变量时快得多!似乎由于某种原因,等待的异步代码(协作线程)使代码更快.
这是代码:
public async Task AutoSendMessages(...) { //Get list of subscriptions plus some initialization LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(numberOfThreads); TaskFactory taskFactory = new TaskFactory(lcts); List<Task> tasks = new List<Task>(); //.... foreach (var sub in subscriptions) { AutoSendData data = new AutoSendData { ServiceId = serviceId,MSISDN = sub.subscriber,IsCrossSellActive = bolCrossSellHeader }; tasks.Add(await taskFactory.StartNew(async (x) => { await SendQuestion(x); },data)); } GC.Collect(); try { Task.WaitAll(tasks.ToArray()); } catch (AggregateException ae) { ae.Handle((ex) => { _logRepo.LogException(1,"",ex); return true; }); } await _autoSendRepo.SetAutoSendingStatusEnd(statusId); } public async Task SendQuestion(object data) { //extract variables from input parameter try { if (isCrossSellActive) { int pieceCount = subscriptionRepo.GetSubscriberCarPieces(curSubscription.service,curSubscription.subscriber).Count(c => c.isConfirmed); foreach (var rule in csRules) { if (rule.Applies) { if (await HttpClientHelper.GetJsonAsync<bool>(url,rule.TargetServiceBaseAddress)) { int noOfAddedPieces = SomeCalculations(); if (noOfAddedPieces > 0) { crossSellRepo.SetPromissedPieces(curSubscription.subscriber,curSubscription.service,rule.TargetShortCode,noOfAddedPieces,rule.ExpirationLimitDays); } } } } } // The rest of the code. (Some db CRUD) await SmsClient.SendSoapMessage(subscriber,smsBody); } catch (Exception ex){//...} }
解决方法
他的评论引起我的注意,期待已经等待的taskFactory.StartNew(…)行,它将新任务顺序添加到“任务”列表中,然后由Task.WaitAll(任务)等待;
起初我在taskFactory.StartNew()之前删除了await关键字,它导致代码处于可怕的故障状态!然后我在taskFactory.StartNew()之前返回await关键字并使用断点调试代码,并且令人惊讶地看到线程在第一个线程到达“SendQuestion”例程内的第一个线程之前一个接一个地运行.当设置“isCrossSellActive”标志时,尽管线程应该执行更多作业,但是先前达到第一个await关键字,从而启用下一个计划任务.但是当它没有设置时,唯一的await关键字是例程的最后一行,所以它最有可能顺序运行到最后.
usr建议在for循环中删除await关键字似乎是正确的但问题是Task.WaitAll()行会在错误的Task列表上等待< Task< void>>而不是任务< void>.我终于使用Task.Run而不是TaskFactory.StartNew,一切都改变了.现在服务运作良好. for循环中的最终代码是:
tasks.Add(Task.Run(async () => { await SendQuestion(data); }));
问题解决了.
谢谢你们.
附:阅读关于Task.Run的这篇文章以及为什么TaskFactory.StartNew是危险的:http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。