107

I am not clear (and can't find documentation clear enough): when using the lock keyword in an async method: will the thread be blocked if the object is already blocked or will it return a task in suspended state (not blocking the thread, and returning when the lock is released)?

In the code below, will the line block the thread? If it blocks the thread (which is what I think), is there an standard not blocking solution? I am considering using AsyncLock, but first I wanted to try for something standard.

private object myLock = new object(); private async Task MyMethod1() { lock (myLock) // <---- will this line cause a return of the current method // as an Await method call would do if myLock was already locked? { //.... } } // other methods that lock on myLock 
2

6 Answers 6

162

This has been disallowed to stop deadlocks (i.e. developers hurting themselves). The best solution I've found is to use semaphores - See this post for details.

Relevant code extract:

private static readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); ... await semaphoreSlim.WaitAsync(); try { await Task.Delay(1000); } finally { semaphoreSlim.Release(); } 
Sign up to request clarification or add additional context in comments.

11 Comments

Is there a reason for making the semaphore static? presumably it would work just as well if the semaphore were an instance variable (assuming you wanted to only lock this instance of the class)
In my case, I have multiple objects that need to report their progress to a database. I want them to be async of course, so a static SemaphoreSlim in my case is the way to go.
Thanks for the answer. The use of SemaphoreSlim, solved my problem.
@MartinKirk, LOL on you : services.RegisterSingleton(new Semaphore(1,1)).AsNamed("lockName") and inject it into any service type(s) that needs synchronization. Use Autofac that supports named/index services
WaitAsync should be in the try block
|
98

In the code below, will the line block the thread?

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. The other reason is that during an await while holding a lock, arbitrary code may execute while the lock is held.

For this reason, the compiler goes out of its way to disallow await expressions within lock blocks. You can still shoot yourself in the foot by using Monitor or other primitives directly, though.

If it blocks the thread (which is what I think), is there an standard not blocking solution?

Yes; the SemaphoreSlim type supports WaitAsync.

10 Comments

Thanks now that I see the problem doing await in lock. What about the opposite: using locks in a method that's called eventually by some async method? My guess is that it's safe, as the locked operation are all completed on the same thread. However, using Monitors directly seems still problematic in this case - but that sucks, because there is usually no way the dev can tell if the code is going to be used in a async/await fashion or not. What's the suggestion?
As long as there's only synchronous code in the lock (or monitor), then it'll work fine. It doesn't matter whether it's called by an asynchronous method or not.
I thought this is what the question was asking (Is a lock inside an async method safe). Thankfully I realized it was a little unclear and scrolled down to these comments. Hahaha.
@OfirD: There's one major problem: the async method may resume executing on a different thread after the await, leading to a situation where a thread releases a lock it never acquired.
@OfirD: Now consider multiple async methods run on the same thread 1: function A acquires the lock and does the await. Then function B runs on the same thread; if it acquires the same lock, it will succeed instead of blocking. Also, in the more general case, function B can depend on some code run by thread 2 that acquires the lock - in that case you end up with a deadlock because thread 1 is holding the lock, waiting for thread 2, which is waiting for thread 1 to release the lock.
|
25

No it won't.

lock is syntactic sugar for Monitor.Enter and Monitor.Exit. lock will keep execution in the method until the lock is released. It does not function like await in any way, shape or form.

3 Comments

Thanks. BTW: I guess applying to my question: "Yes, the thread will block", is the answer that I am looking for.
lock() spins for a short period and then blocks. If it just kept spinning it'd change inefficient code into inefficient time-and-power-eating code.
A bad use of the word "spin" on my part. I just meant to say that execution does not leave the method like it does with await. I will rephrase. Thanks @CoryNelson.
1

You task will not return in suspended state. It will wait until myLock is unlocked to run the code within lock statement. It will happen no matter what C# asynchronous model you use.

In other words, no two threads will be able to run statements inside the lock. Unless, there are many different instances of myLock object.

Comments

1

Can't tell what was in 2013, but in 2024 and C# 12 it is perfectly OK to lock() in an async function as far as you don't await inside it.

Without await, the code is executed in one thread and compiler is happy to lock().

2 Comments

The same was true in 2013 as well. Nothing changed since then.
I would like to say that lock() uses System.Threading.Lock class that on LockScope() returns ref struct scope that's allocates on stack only. because async methods are state machines where all local variables becomes fields, new lock() statement cannot have awaited code inside of it's scope
-2

what i do is await Task.Run and call it without await inside. Like this:

 return await Task.Run<IEnumerable<ViewsDataModel>?>(() => { lock (context) { return context.ViewsDataModels.Where(o => o.Owner == userId).ToList(); } }); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.