我们有一个ASP.NET MVC网站,并将所有文本存储在MongoDB中。 LocalizationTextManager类负责提供这些文本并在内部缓存它们。通常,该方法非常快(<5ms),如果结果在缓存中则更快。
我们有两个方法:GetString和GetStringAsync。 GetStringAsync是首选,但我们在Razor中使用GetString方法,或者在一些不在异步上下文中的罕见情况下使用。
MongoDB有一个异步驱动程序,我需要非同步地实现它。因此我们尝试了几种方法。我确保在代码中的任何地方设置ConfigureAwait(false)。
FindOrAddTextFromRepositoryAsync(key).Result;
Task.Run(async () => await FindOrAddTextFromRepositoryAsync(key)).Result;
Task.Run(async () => await FindOrAddTextFromRepositoryAsync(key).ConfigureAwait(false)).Result;
我知道在任务中我不需要ConfigureAwait(false)(因为不应该有同步上下文)。
我刚刚部署了网站,并在部署后挂起。几次重启后,它正在运行。我之前做了转储,发现有很多这些方法调用:
w3wp(4).DMP中的以下线程正在System.Threading.Monitor.Wait中等待。 ~100线程被阻止:
mscorlib_ni!System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)+3ec
mscorlib_ni!System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)+db
mscorlib_ni!System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken)+24a
mscorlib_ni!System.Threading.Tasks.Task`1[[System.__Canon, mscorlib]].GetResultCore(Boolean)+36
GP.Components.Globalization.LocalizationTextManager.GetString(System.String, System.String)+2f4
GP.Components.Globalization.LocalizationTextManager.GetString(System.String, System.Globalization.CultureInfo)+8a
我的问题是:如何正确实施?另一个想法是使用LimitedThreadsScheduler
来确保它不会大量并行化。
答案 0 :(得分:1)
代码中的主要问题是您的代码不是异步的!
对于您创建的每个Task
,您明确调用Result
属性
.Result;
导致阻止当前线程,直到任务完成。
如果您需要处理Task.Complete event
,则可以使用Task
类continuation method的to wait the tasks are pending或静态方法。只是不要阻止你的任务:
.ContinueWith( (t) => { Console.WriteLine(t.Result); },
TaskContinuationOptions.OnlyOnRanToCompletion);
或:
Task.WaitAll(tasks);
正如我所看到的,在跟踪GetString
中,非异步版本正在运行并等待结果,因此其他线程无法执行任何操作。我建议您尝试通过setting the MaximumThreads
for default thread pool调整性能Tasks
,并将不同任务调度程序的同步和异步代码分开,这样它们就不会相互阻塞。其他任务选项在此处解释:Task.Run vs Task.Factory.StartNew
至于你最后的问题,这是一篇关于How to: Create a Task Scheduler That Limits Concurrency
的精彩文章,所以你可以尝试从那里开始。