admin管理员组

文章数量:1122846

The 2020 standard says (23.9.9/4.1):

If launch::async is set in policy, calls invoke(decay-copy(std::forward(f)), decaycopy( std::forward(args))...) (20.14.4, 32.4.3.3) as if in a new thread of execution [...].

Does "as if" in a new thread guarantee that I can use std::mutex to protect resources accessed by multiple parallel thingies started with async? It works very well, but that means nothing when it comes to concurrency, does it.

Even though this seems the intent and many tutorials do that, I'm asking because ordinary mutexes absolutely must not be locked twice from the same thread. And because mutexes and threads may map 1:1 to system resources, some magic must be done if two async thingies may interleave on the same system thread and lock the same mutex.1

Tangentially related was a similar issue with thread_local variables in async thingies that were re-using threads from a thread pool. Apparently, the consensus was formed that "as if" here implies "as-if-thread local", i.e., the behavior should indeed completely mimic running on different threads. (Re-using a thread linearly for a next async thingie after some predecessor has finished should not be an issue for mutexes anyway because a thread and presumably also a thingie must not end while still locking a mutex, i.e., all mutexes are unlocked by the time the new thingie starts.)

Presumably, "as if" in a new thread is the case for mutexes as well.

Is it?


1 An example for such interleaving were Java's Green threads which all mapped to the same OS thread.

The 2020 standard says (23.9.9/4.1):

If launch::async is set in policy, calls invoke(decay-copy(std::forward(f)), decaycopy( std::forward(args))...) (20.14.4, 32.4.3.3) as if in a new thread of execution [...].

Does "as if" in a new thread guarantee that I can use std::mutex to protect resources accessed by multiple parallel thingies started with async? It works very well, but that means nothing when it comes to concurrency, does it.

Even though this seems the intent and many tutorials do that, I'm asking because ordinary mutexes absolutely must not be locked twice from the same thread. And because mutexes and threads may map 1:1 to system resources, some magic must be done if two async thingies may interleave on the same system thread and lock the same mutex.1

Tangentially related was a similar issue with thread_local variables in async thingies that were re-using threads from a thread pool. Apparently, the consensus was formed that "as if" here implies "as-if-thread local", i.e., the behavior should indeed completely mimic running on different threads. (Re-using a thread linearly for a next async thingie after some predecessor has finished should not be an issue for mutexes anyway because a thread and presumably also a thingie must not end while still locking a mutex, i.e., all mutexes are unlocked by the time the new thingie starts.)

Presumably, "as if" in a new thread is the case for mutexes as well.

Is it?


1 An example for such interleaving were Java's Green threads which all mapped to the same OS thread.

Share Improve this question asked Nov 21, 2024 at 12:38 Peter - Reinstate MonicaPeter - Reinstate Monica 16k4 gold badges41 silver badges67 bronze badges 6
  • 1 For all available implementations ? yes. Is it guaranteed by the standard ? I don't know. – Ahmed AEK Commented Nov 21, 2024 at 12:53
  • 1 You should just assume this is new thread and write code in thread safe manner. – Marek R Commented Nov 21, 2024 at 12:54
  • 1 The whole point of the phrase "as if" is that the behavior must be the same in all observable respects, right? A program which would have well-defined behavior under the standard if an actual new thread were used, must have that same observable behavior regardless of how it is actually implemented. So I don't see any reason to doubt that the standard allows you to lock mutexes. Now, a separate question is whether all existing implementations (or at least those which you need to target) actually get it right. – Nate Eldredge Commented Nov 21, 2024 at 13:15
  • 1 Unless called with std::launch::async as its first argument, you should not lock the mutex in calling thread. If std::launch::deferred is specified, the first thread to wait on the future runs the function. If the launch parameter is not specified, platform can choose to deffer. So, if the mutex is not multi-lockable(eg. std::recursive_mutex), it can only be locked within a launch::async call. – Red.Wave Commented Nov 21, 2024 at 15:33
  • @Red.Wave Considering the implications of potentially deferred tasks is a valid point; but I don't lock in the calling code, I only lock in the created tasks. Deferred execution then is not harmful, everything will simply run sequentially (in the future.get() chain) which is one edge case of allowed execution order anyway. [Hm, at second thought it may be that everything in the main thread is a problem, true!] – Peter - Reinstate Monica Commented Nov 21, 2024 at 16:16
 |  Show 1 more comment

1 Answer 1

Reset to default 6

"as if in a new thread" should be read in the context of a platform-neutral C++ standard. From the C++ perspective, the thread must be new. That includes C++ objects like std::mutex. But it's unspecified whether std::thread maps to OS threads, and it's unspecified if OS threads can be reused. As far as the standard is concerned, it's even possible that the OS thread of a std::thread is reused for std::async.

本文标签: cstdasync does quotas if in a new threadquot guarantee that it is safe to use mutexesStack Overflow