Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locking async task c#

public class MyDbContext : DbContext
{
    ....

    static readonly object lockObject = new object();

    public override int SaveChanges()
    {
        lock (lockObject)
            return base.SaveChanges();
    }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        lock (lockObject)
            return base.SaveChangesAsync(cancellationToken);
    }       
}

I want to use DbContext in multiple threads, but the underlying database has a problem if two threads are writing at the same time. So I am using lockObject to prevent two SaveChanges() at the same time.

That works fine in SaveChanges(), but I don't know how to do the same in SaveChangesAsync(). I think the solution above is wrong. How can I apply the lock to SaveChangesAsync?

Thank you!

like image 499
csname1910 Avatar asked Oct 29 '19 07:10

csname1910


People also ask

Can we use lock in async await?

Using a plain lock won't work because you can't await inside of a lock. I decided to try a SemaphoreSlim as that has a async method.

Can we use lock in async await C#?

Async locks Trying to release such a lock on any thread other than the one that acquired it will result in an exception. In fact, the C# compiler will even generate a build error if you try to await inside a lock statement to save you from trouble.

Can you wrap asynchronous code in a lock statement?

Technically, yes, but it won't work as you expect. There are two reasons why thread-affine locks don't play well with async . One is that (in the general case), an async method may not resume on the same thread, so it would try to release a lock it doesn't own while the other thread holds the lock forever.

What is an AsyncLock?

AsyncLock is an open source library/wrapper around SemaphoreSlim that adds reëntrance and recursion without taking await async / await functionality. Using AsyncLock couldn't be simpler: simply swap any lock (lockObject) { ... } calls with using (lockObject.


1 Answers

From https://blog.cdemi.io/async-waiting-inside-c-sharp-locks/ you can use:

public class MyDbContext 
{
    static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);

    public override int SaveChanges()
    {
        semaphoreSlim.Wait();
        try
        {
            return base.SaveChanges();
        }
        finally
        {
            //When the task is ready, release the semaphore. It is vital to ALWAYS release the semaphore when we are ready, or else we will end up with a Semaphore that is forever locked.
            //This is why it is important to do the Release within a try...finally clause; program execution may crash or take a different path, this way you are guaranteed execution
            semaphoreSlim.Release();
        }
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        await semaphoreSlim.WaitAsync(cancellationToken);
        try
        {
            return await base.SaveChangesAsync();
        }
        finally
        {
            //When the task is ready, release the semaphore. It is vital to ALWAYS release the semaphore when we are ready, or else we will end up with a Semaphore that is forever locked.
            //This is why it is important to do the Release within a try...finally clause; program execution may crash or take a different path, this way you are guaranteed execution
            semaphoreSlim.Release();
        }
    }
}
like image 141
Ive Avatar answered Sep 30 '22 05:09

Ive