-1

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 internet 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

Mohammad Aghazadeh
  • 2,108
  • 3
  • 9
  • 20
Freddyy
  • 59
  • 2
  • 4
  • Did you already read this? https://learn.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development – Klaus Gütter Jan 14 '23 at 05:49
  • 1
    I see a bunch of `Task` objects, but not a single `await`. Here is a pretty basic rule of thumb: If you have a synchronization context, never, ever... ever... use `.Result`, `.GetAwaiter().GetResult()`, `.Wait()`. Always use `await`. There are special circumstances where you could use those methods, but not in your cases above. – Andy Jan 14 '23 at 06:29
  • Does this answer your question? [How to call asynchronous method from synchronous method in C#?](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c) – MD Zand Jan 14 '23 at 06:54

1 Answers1

0

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.