MongoDB 2.0驱动程序和非异步代码的潜在死锁
我们有一个ASP.NET MVC网站,并将所有文本存储在MongoDB中。 LocalizationTextManager类负责提供这些文本并在内部缓存它们。通常,这种方法非常快(< 5ms),如果结果存在于缓存中,则速度更快。MongoDB 2.0驱动程序和非异步代码的潜在死锁
我们有两个方法: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 Thread threaded:
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
来确保它不会严重并行化。
你的代码中的主要问题是你的代码不是异步的!
对于每个Task
你创建你显式调用Result
财产
.Result;
导致阻塞当前线程,直到任务完成。
如果您需要处理Task.Complete event
,您可以使用continuation method或Task
类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的性能,将使用setting the MaximumThreads
for default thread pool,并为不同的任务调度程序分配同步和异步代码,以便它们不会相互阻塞。其他选项的任务开始解释这里:Task.Run vs Task.Factory.StartNew
至于你的问题在最后,这里是一个关于How to: Create a Task Scheduler That Limits Concurrency
伟大的文章,所以你可以尝试从那里开始。
我知道,这正是我所做的;) – SebastianStehle
更新了一些想法的答案 – VMAtm
@VMAtm只是要清楚,你会推荐什么?使用委托来分配值,然后使用Task.Wait Any? –