简体   繁体   中英

Async/Await snippet deadlocks when targeting MS-SQL-Server (as expected) but doesn't deadlock when targeting Oracle (unexpected)

Forward Warning:

I'm fully aware that the use of .Result in the context shown below is flat-out wrong and should be avoided at all costs. I know what the correct approach is.

The nature of this question has more to do with the infrastructure of db-drivers: Oracle vs Ms-Sql-Server. It's an academic question for the most part.


System.Web.MVC.dll: ver. 5.2.3.0 (MVC5)
EntityFramework.dll: ver. 6.1.3
EntityFramework.SqlServer.dll: ver. 6.1.3
Oracle.ManagedDataAccess: ver. 4.121.2.0 (oracle driver ver 12.2.0.1.0)
Oracle.ManagedDataAccess.EntityFramework: ver. 6.121.2.0  (oracle driver ver 12.2.0.1.0)

Consider the following repository and its invocation within an ASP.NET MVC controller:

 public class MyRepository {
     [...]
     public async Task<SomeEntity> GetFirstFooAsync() => await new SomeEFContext().FooTable.FirstOrDefaultAsync();
 }

 public class SomeController : Controller {
     public ActionResult SomeAction() {
          var result = new MyRepository.GetFirstFooAsync().Result; //<-- crappy approach I know
          [...]
     }
 }

The above piece of code doesn't deadlock when the underlying db is Oracle. But this exact same piece of code causes a deadlock when targeting MS-Sql-Server via the associated driver provided by MS for EF.

The deadlock is the expected behaviour for such crappy code

  • Why the oracle-driver doesn't cause a deadlock? Does it use .ConfigureAwait(false) internally or some other tweak?

  • If the oracle driver indeed uses a tweak should I be concerned that it might have undesirable side-effects even when programming the invocation appropriately

like so:

 public class SomeController : Controller {
     public async Task<ActionResult> SomeAction() {
          var result = await new MyRepository.GetFirstFooAsync();
          [...]
     }
 }

I'm asking this because I'm under the impression that internet wisdom is against using .ConfigureAwait(false) "partially" and I should thus use .ConfigureAwait(false) in all other async invocations (if any) even if they target other components. I'm not sure I understand/interpret the aforementioned recommendation properly however - I would be happy to stand corrected. If I'm right to be concerned please be so kind to provide an example where partial usage of .ConfigureAwait(false) will cause problems and also provide an example on how .ConfigureAwait(false) should be used when multiple Async() invocations are involved.

ConfigureAwait(false) will not prevent that deadlock.

I'll speculate that Oracle is probably async over sync code wrapped inside Task.Run (or something similar).

ConfigureAwait(false) is not needed on ASP.NET Core (because it doesn't have a synchronization context) and should not be used inside the action method because the context is needed after exiting the action method.

You might try this (I haven't) to see if it works with both providers:

public class SomeController : Controller
{
    public ActionResult SomeAction()
    {
        var synchronizationContext = SynchronizationContext.Current;
        try
        {
            var result = new MyRepository.GetFirstFooAsync().Result;
            [...]
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(synchronizationContext);
        }
    }
}

https://community.oracle.com/thread/4092961

Well crap. Oracle is pulling our feet. They don't have any respect for async/await. They internally resort to blocking calls as if async/await was not even being used anywhere. 'Great'.

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