Skip to content Skip to sidebar Skip to footer

How to Run Same Task Again C3

Understanding Async, Fugitive Deadlocks in C#

Typical lawmaking that might popular up in a C# codebase and can exist pretty dangerous.

Tasks or Threads?

What Does await Really Practise?

The proper async / look version:

                public                                     async Task<Cord> DownloadStringV1(String url)
{
// good code
var asking = await HttpClient.GetAsync(url);
var download = await request.Content.ReadAsStringAsync();
return download;
}

ContinueWith / Unwrap version (this is still async):

                public Task<Cord> DownloadStringV2(String url)                  
{
// okay lawmaking
var request = HttpClient.GetAsync(url);
var download = asking.ContinueWith(http =>
http.Result.Content.ReadAsStringAsync());
return download.Unwrap();
}

Lawmaking that might work in some contexts…

Sync version, blocks the thread, non safe:

                public Cord DownloadStringV3(String url)                  
{
// Non SAFE, instant deadlock when chosen from UI thread
// deadlock when chosen from threadpool, works fine on console
var request = HttpClient.GetAsync(url).Upshot;
var download = request.Content.ReadAsStringAsync().Result;
render download;
}
  1. Calling HttpClient.GetAsync(url) will create the request, it might run some role of it synchronously, but at some point information technology reaches the office where it needs to offload the work to the networking API from the OS.
  2. This is where it will create a Chore and return it in an incomplete state, so that you tin schedule a continuation.
  3. Only instead you accept the Event holding, which will blocks the thread until the task completes. This just defeated the whole purpose of async, the thread can no longer work on other tasks, information technology's blocked until the request finishes.
  • If you are calling from UI thread, you will deadlock instantly, as the chore is queued for the UI thread which gets blocked when it reaches the Event holding.
  • If chosen from threadpool thread then a theadpool thread is blocked, which will lead to a deadlock if the piece of work load is high enough. If all threads are blocked in the threadpool then there will be nobody to complete the Chore.
  • But this example will work if y'all're calling from a main or defended thread. (which does non vest to threadpool and does non have syncronization context)

Sync version, defeats the purpose, blocks the calling thread and definitely non prophylactic:

                public String DownloadStringV4(String url)                  
{
// Non SAFE, deadlock when called from threadpool
// works fine on UI thread or console main
render Task.Run(async () => {
var request = await HttpClient.GetAsync(url);
var download = await request.Content.ReadAsStringAsync();
render download;
}).Result;
}
  • If you have a classic ASP.Cyberspace application or a UI application, yous tin can call async functions from sync function using this method, then update the UI based on the issue, with the caveat that this blocks the UI or IIS managed thread until the work is done. In case of the IIS thread this is not a huge problem as the asking cannot complete until the work is not done, but in example of a UI thread this would brand the UI unresponsive.
  • If this code is called from a threadpool thread, then over again it will lead to a deadlock if the work load is high enough because information technology'due south blocking a threadpool thread which might be necessary for completing the task. All-time is to avoid writing code like this, peculiarly in context of library where you have no control over the context your code gets called from.

Deadlock version. Dont write this:

                public String DownloadStringV5(String url)                  
{
// Really Really BAD CODE,
// guaranteed deadlock
render Job.Run(() => {
var request = HttpClient.GetAsync(url).Result;
var download = request.Content.ReadAsStringAsync().Result;
return download;
}).Result;
}

What Causes a Deadlock?

Aye! This causes a deadlock!

                public String GetSqlConnString(RubrikkUser user, RubrikkDb db)                  
{
// deadlock if called from threadpool,
// works fine on UI thread, works fine from console main
return Task.Run(() =>
GetSqlConnStringAsync(user, db)).Result;
}

This still causes a deadlock!

                public Cord GetSqlConnString(RubrikkUser user, RubrikkDb db)                  
{
// deadlock from UI thread, deadlock if called from threadpool,
// works fine from panel main
render GetSqlConnStringAsync(user, db).Result;
}

SyncronizationContext? TaskScheduler?

Tasks need to be scheduled somehow.
  • In console applications by default you don't accept a synchronization context, but you have a principal thread. Tasks will be queued using the default TaskScheduler and will be executed on the thread pool. You can freely block your main thread it will simply stop executing.
  • If you create a custom thread, by default y'all dont accept a syncronization context, it'south simply like having a console application. Tasks get executed on the thread pool and y'all tin can block your custom thread.
  • If you are in a thread pool thread, and then all following tasks are likewise executed on the thread pool thread, but if you take blocking code hither then the threadpool will run out of threads, and you will deadlock.
  • If you are in a desktop UI thread, you have a synchronization context, and by default tasks are queued for execution on the UI thread. Queued tasks are executed one by i. If you block the UI thread there is cipher left to execute tasks and you have a deadlock.
  • If yous're writing a dotnet cadre spider web awarding, yous're basically running everything on the thread puddle. Any blocking code will block the thread pool and any .Effect will lead to a deadlock.
  • If you're writing a ASP.NET web application, and then you lot have theads managed by IIS which will allocate one for each asking. Each of these threads has its ain syncronization context. Tasks get scheduled for these threads by default. Y'all demand to manually schedule for the threadpool for parallel execution. If you telephone call .Outcome on a task which is enqueued for the request thread, yous will instantly deadlock.
  • If you're writing a library, you have no idea what code is calling your code, and mixing async lawmaking with sync code, or calling .Result volition nearly certainly make an application deadlock. Never mix async and sync lawmaking in a library.

How to Write Good Async Code?

  • Just call async code only from async lawmaking. (dont mix sync with async)
  • Never block in async code. (never .Result, never lock)
  • If you demand a lock, use SemaphoreSlim.WaitAsync()
  • Use async/await when dealing with Tasks, instead of ContinueWith/Unwrap, information technology makes the code cleaner.
  • It'southward okay to provide both sync and async version of API, merely never call one from the other. (this is ane of the rare cases when lawmaking duplication is acceptable)

Debugging Methodology

Retentiveness Dumps Assist a Lot!

Reproducing the Deadlock

Some Corrections:

schmidtmorold.blogspot.com

Source: https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d

Post a Comment for "How to Run Same Task Again C3"