Reputation: 2457
we have an ASP.NET MVC website and store all our texts in MongoDB. The class LocalizationTextManager is responsible to provide these texts and caches them internally. Typically this method is very fast ( < 5ms) and even faster if the result is in the cache.
We have two methods: GetString and GetStringAsync. GetStringAsync is preferred but we use the GetString method within Razor for example or in some rare situations where are not in an async context.
MongoDB has an async driver and I need to implement it non synchronously. Therefore we tried several approaches. I ensured that I set ConfigureAwait(false) anywhere in my code.
FindOrAddTextFromRepositoryAsync(key).Result;
Task.Run(async () => await FindOrAddTextFromRepositoryAsync(key)).Result;
Task.Run(async () => await FindOrAddTextFromRepositoryAsync(key).ConfigureAwait(false)).Result;
I know that I dont need ConfigureAwait(false) within the task (because there should be no synchronization-context).
I just deployed the website and it hangs after deployment. After several restarts of the process it was working. I made dumps before and found out that there are a lot of these method calls:
The following threads in w3wp (4).DMP are waiting in System.Threading.Monitor.Wait. ~100 Thread blocked:
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
My question is: How do I implement it correctly? Another idea is to use a LimitedThreadsScheduler
to ensure that it is not parallelized heavily.
Upvotes: 2
Views: 505
Reputation: 28366
The main issue in your code is that your code isn't asynchronous!
For each Task
you create you explicitly call the Result
property
.Result;
which leads to block the current thread until the task is done.
If you need to handle the Task.Complete event
, you can use a continuation method or static methods of Task
class to wait the tasks are pending. Simply do not block your tasks:
.ContinueWith( (t) => { Console.WriteLine(t.Result); },
TaskContinuationOptions.OnlyOnRanToCompletion);
or:
Task.WaitAll(tasks);
As I see, in the trace GetString
, non-async version is running and waits the result, so other threads can't do anything. I suggest you to try to tune up the performance by setting the MaximumThreads
for default thread pool which is being used for Tasks
, and split up the sync and async code for different task schedulers so they doesn't block each other. Other options of tasks start explained here: Task.Run vs Task.Factory.StartNew
As for your question at the end, here is a great article about How to: Create a Task Scheduler That Limits Concurrency
, so you can try to start from there.
Upvotes: 1