简体   繁体   中英

C# Convert async code to run sync in certain method(Constructor/Main/PropertyGetter/PropertySetter) without possibility of deadlock

I have a bunch of async code, I have tried to expand my asyc code as large as possible in my codebase. I am here looking for an safe approach to convert async code to async (wait it finish then return result). I have tied looking on the inte.net but they either way to complex or may cause deadlocks.

Here is my code:

 protected virtual DbData GetDbData()
 {           
    return StorageProvider.RefreshAsync().Result;       
 }

The GetDbData will be used as a property getter something like:

 public override DbData Data
 {
     get => GetDbData();
     set => SetDbData(value);
 }

and in constructor

public CachedDataManager(IStorageProvider storageProvider) : base(storageProvider)
{
   _cachedData = StorageProvider.RefreshAsync().Result;       
}

or for the async method without return value

public CachedDataManager(IStorageProvider storageProvider) : base(storageProvider)
{          
    DoSomeWorkAsync().GetAwaiter().GetResult();
}

private Task DoSomeWorkAsync()
{
   //Assume heavy load.
   Task.Delay(5000);
   return Task.CompletedTask;          
}

Result may causing a deadlock because of the SynchronizationContext when calling in the UI thead (what I know)

All the other solution told me to expand async code as far as possible, I tried, but I can't do it with constructor/property getter

Are there exist some solution similar to Task.Result / Task.Wait / Task.RunSynchronously that does not causes any problems(eg deadlock)?

Also I am not sure about DoSomeWorkAsync().GetAwaiter().GetResult(); it does causes problems with SynchronizationContext or not. I am a noob on this. Please help

A task (what async methods return) can contain every type of logic. Suppose you are calling an async Task method from the Thread A, and you want to synchronously wait for the task to complete. Now let's think carefully what could be the conditions for a deadlock: basically we have to guarantee that the Task do not schedule anything "asynchronously" on the Thread A. If that's the case you can safely call task.GetAwaiter().GetResult(). If not you can't. It's a conceptual limit. There is not a general way to avoid deadlocks.

For example suppose that the Thread A is the UI thread. If the async Task contains a "real" await operation (that is, an await over a Task that is not completed yet, such as Task.Delay(100)), we are already in a bad spot: why? Because the code after the await is wrapped into an Action and scheduled through the Synchronization Context in the UI Thread. So the task is waiting for some code to execute in the UI thread, but the UI thread is waiting for the task to complete

Another example: suppouse the Task method we are awaiting is not async (so contains synchronous code, like Task.Run(VoidMethod)) and that at some point there is a call to Dispatcher.Invoke(). We still have a deadlock: the task to complete should wait for some code to be executed on the UI Thread. But the UI Thread is waiting for that task to complete..

Side note. While in the first example we can do some tricks such as change the Synchronization context temporarily in order to avoid the continuations to run on the UI Thread, in the second example there are no chance to make it work, it's conceptually wrong if you thing about it. So there isn't a general way.

It really depends.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM