4

In a multi-threaded environment, we must consider concurrent access to writable resources. A common approach is to use Monitor or its shorthand form lock.

Task is at a different abstraction level than Thread. A task may run on a thread of its own (and according to the logs, they do so in our application), but that is not guaranteed. See e.g. What is the difference between task and thread?:

If the value you are waiting for comes from the filesystem or a database or the network, then there is no need for a thread to sit around and wait for the data when it can be servicing other requests. Instead, the Task might register a callback to receive the value(s) when they're ready.

That is, that kind of Task somehow shares a Thread with other running code (I must admit that I do not understand how that works in detail, currently it looks to me like a specialization of the "famous" DoEvents).

Consequently, Monitor won't be able to distinguish between them, and - because Monitor can be re-entrant - allow both of them access the resource. That is, Monitor "fails."

Examples with Threads typically use Monitor nonetheless. So I want to ask how I can be sure that Monitor is safe with a Task (or: how can I be sure that a Task is running on a Thread of its own).

3
  • Do you have a link to a web reference that describes this problem? I don't think you are correct about this. A thread is a thread; if your thread is being "shared" with other code, it means that it's the same thread, and the lock no longer applies. That's why re-entrancy exists; it allows a method with a critical section to do things like call itself recursively. – Robert Harvey Jan 18 '17 at 15:58
  • I wonder if there's a standard lock class with asynchronous lock()... I think it could work – Bwmat Jan 19 '17 at 17:04
  • 1
    @Bwmat There is. SemaphoreSlim. (You could also write your own, if you wanted to.) – Servy Jan 19 '17 at 20:32
6

So I want to ask how I can be sure that Monitor is safe with a Task (or: how can I be sure that a Task is running on a Thread of its own).

You don't. You shouldn't be using a Monitor in an asynchronous operation precisely because you can't guarantee this (and wouldn't want to even if you could).

The solution is to use different synchronization mechanisms that are designed to function appropriately in an asynchronous context. They need to not use thread affinity, for example, and you'll also want them to be themselves asynchronous, rather than synchronously waiting for access to the critical section.

The answer is to use SemaphoreSlim, which has a WaitAsync method that you can await. Assuming you have a traditional critical section, you can set the semaphore to only allow 1 concurrent operation. Using this, if a thread gets access to the semaphore, then starts an asynchronous operation and goes off to do something else that also tries to access the semaphore, it won't have access. It also means you won't have any problems releasing the semaphore from a different thread than you acquired it from.

2
  • That's what I suspected. But out there in the wilderness, I see articles like that one from Microsoft: msdn.microsoft.com/en-us/library/… ... – Bernhard Hiller Jan 20 '17 at 8:07
  • @BernhardHiller That code is only ever using locks in a synchronous context. It takes out a lock, performs some synchronous operations, and then releases the lock. There's no asynchrony involved in the lock at all. It's also just generally pretty bad code. There are far more effective ways of doing what those examples do. I'd strongly discourage even looking at them as an example; they're simply not code to emulate. – Servy Jan 20 '17 at 14:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.