2

Question 1: Is the lock really necessary in this situation?

var combinedResponse = new Response();
foreach (var server in servers)
{
    Response r = await sqlExecutor.ExecQuery(query, server);
    lock (combinedResponse)  // is the lock necessary here? 
    {
        combinedResponse.Merge(r); //knowing that we must not merge 2 results in parallel
    }
}

I don't know which is true?

1) the codes after await is executed in the same thread with the codes before await (=> so the lock is not neccessary)

2) the codes after await might be executed in a totally different thread than the codes before await (=> so the lock is neccessary)

Question 2:

In my case the ExecQuery is an IO-bound task, it won't spawn any additional thread. But if it wasn't the case (if ExecQuery was a CPU-bound task which might spawn many additional threads) then would I need to lock after await?

Hiep
  • 2,483
  • 1
  • 25
  • 32
  • 2
    You never need to use `lock` after `await`, that simply doesn't make any sense. There are no multiple threads reading/writing at the same time. That said, whether a new thread or the same is used is determined by the current `TaskScheduler` and it doesn't make a difference at all here – Camilo Terevinto Oct 21 '18 at 13:17
  • 4
    Both statements that you say you do not know if they are true are **false**. Whether the continuation of an await executes on a *different* thread or not depends on the *context*. Neither has anything to do with whether the code either before or after the await is *thread safe*. – Eric Lippert Oct 21 '18 at 13:32
  • @EricLippert do you mean that after await we cannot be sure in which thread we will be, so it is better to keep the lock? – Hiep Oct 21 '18 at 13:54
  • @CamiloTerevinto if Merge(r1) is a long operation, if r2=ExecQuery() is finished before Merge(r1), I'm afraid that Merge(r2) will be called while Merge(r1) didn't finish yet, in this case lock make sense – Hiep Oct 21 '18 at 14:00
  • 1
    @Hiep: No, that is not at all what I am saying. You seem to have some false beliefs about both locks and awaits. Can you explain what you believe a lock is for? Locks are for two things: (1) creating critical sections, and (2) enforcing ordering constraints. Can you explain either why you believe that a lock is for something other than those two things, or, which of those two things you believe you need after an await? – Eric Lippert Oct 21 '18 at 14:01
  • @Hiep What? That could only happen if Merge was a non-awaited asynchronous operation, which wouldn't make sense. You really need to learn the basics of `async` and `await` it seems – Camilo Terevinto Oct 21 '18 at 14:02
  • 2
    @EricLippert It seems that the OP thinks that `await` could lead to parallel operations in the posted code – Camilo Terevinto Oct 21 '18 at 14:05
  • 2
    But regardless, remember that the purpose of await is to *increase the amount of time threads spend working*, and locks are to *stop contending threads from working*. If you feel like you need to mix them then something is very wrong in your asynchronous workflow. Redesign the workflow so that you don't need to lock on long-running operations. – Eric Lippert Oct 21 '18 at 14:06
  • @CamiloTerevinto thanks, you are right, I'm trying to learn the basic here, my statement and codes might wrong in many way, this is while I need your help.. d – Hiep Oct 21 '18 at 14:10
  • 1
    @CamiloTerevinto exactly, I'm not sure if await could lead to parallel operations in the posted code, so I put the lock.. I see it is wrong, thank you – Hiep Oct 21 '18 at 14:34

1 Answers1

2

await does not create a new thread, both the code that's awaited and the code after await will run in same thread unless you create additional threads in ExecQuery. Whether you need lock depends on how you call this method, if you are calling it from multiple threads then you will need to synchronize thread's access to the code after await using lock or something else.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • It think people often conflate Multitasking (the umbrella term) with Multithreading (a way to implement Multtiasking). Mostly because befor async/await, Multithreading was the only viable implementation. Wich is why I talk about Multitasking unless I really mean threading. – Christopher Oct 21 '18 at 13:33
  • If the code continues to run on the same thread is depended of the context. It might run on another thread. If you use this method from multiple threads you don't need the lock either, because it is a local variable. – Creepin Oct 21 '18 at 14:45
  • @Creepin: What does it matter whether it is a local variable or not? First, **a local variable can be shared memory**. "Local" does not mean "on the stack". Local means that *the name has local scope*. Second, the local could *refer* to shared memory. – Eric Lippert Oct 21 '18 at 16:02
  • @EricLippert So you mean `combinedResponse. Merge(r)` needs to be locked in this concrete case if the code snippet was a function and was called from multiple threads? I don't see how multiple threads could be a problem for the local `combinedResult` – Creepin Oct 21 '18 at 16:20
  • @Creepin it's unlikely but possible. each thread gets its own stack but you can pass a local variable by ref to another method. – Selman Genç Oct 21 '18 at 16:34
  • @SelmanGenc I see, this could be the case in general, yes. Eric is also right with his remarks. But in this concrete situation of the authors snippet, `combinedResult` is a local variable that is not shared memory and does not refer to shared memory. So I may correct my statement in saying "... because it is a local variable that is not and does not refer to shared memory in this case" – Creepin Oct 21 '18 at 16:42
  • 1
    I just want to eliminate the myth that local variables are automatically threadsafe **because they are local variables**. There is nothing magical about local variables. They're just like any other variable: they can be shared storage, and they can refer to shared storage. Like every other variable, they've got to be synchronized if they are shared storage, or if they refer to shared storage, or if there is an ordering constraint that must be made with respect to another shared variable. – Eric Lippert Oct 21 '18 at 16:50
  • Yes, that is right, I was not exact enough. That being said, the lock is not required in THIS case if the the function is called in parallel by multiple threads. – Creepin Oct 21 '18 at 17:03