MongoDB 2.0驱动程序和非异步代码的潜在死锁

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 methodTaskto 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伟大的文章,所以你可以尝试从那里开始。

+0

我知道,这正是我所做的;) – SebastianStehle

+0

更新了一些想法的答案 – VMAtm

+0

@VMAtm只是要清楚,你会推荐什么?使用委托来分配值,然后使用Task.Wait Any? –