调用内部可变lambda时std :: function是否锁定互斥锁? [英] Does std::function lock a mutex when calling an internal mutable lambda?

查看:63
本文介绍了调用内部可变lambda时std :: function是否锁定互斥锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

除非另有说明,否则C ++标准库提供有关其类型的以下保证:

The C++ Standard library provides the following guarantees about its types unless stated otherwise:

(1)读操作(即在 const 对象上进行操作)是线程安全的.这意味着只要没有线程同时向该对象写入(应用非 const 操作),就可以在没有竞争条件的情况下同时从一个对象读取多个线程.

(1) Read operations (i.e. working on a const object) are thread-safe. That means multiple threads may read from an object at the same time without race conditions as long as no thread is writing (applying a non-const operation) to the object at the same time.

(2)只要每个对象一次最多只能访问一个线程,则多个线程可以同时读取和写入任意对象.

(2) Multiple threads may read and write arbitrary objects at the same time as long as each object is only accessed by at most one thread at a time.

标准库对用户类型的要求相同.(您可以在 GotW#95 或观看

The standard library requires the same guarantees by user types. (You can read about this stuff in GotW #95 or watch Herb at C++ and Beyond 2012 explaining about this.)

现在我的问题是,是否以下结论正确:由于 std :: function operator() const 成员函数,它必须是线程安全的.如果在构造过程中传入的函子具有const operator()成员函数,则 std :: function 对象可以假定它是线程安全的,并仅转发调用.但是,如果构造时传递给它的函子具有可变的 operator(),则此操作不需要是线程安全的,但 std :: function 仍需要是因为呼叫运算子仍为const.因此, std :: function 必须在外部同步对存储的可变函子的调用,因此必须使用互斥锁.如果将可变的lambda传递给 std :: function 的构造函数,则这意味着性能开销.

Now my question is, if the following conclusion is correct: Since the operator() of std::function is a const member function, it is required to be thread-safe. If the functor passed in at construction has a const operator() member function, then the std::function object can assume it to be thread-safe and just forward the call. However, if the functor passed to it at construction has a mutable operator(), then this operation does not need to be thread-safe, but std::function still needs to be, because the call operator remains const. Hence std::function must externally synchronize calls to the stored mutable functor and hence use a mutex. This implies a performance overhead in case of passing mutable lambdas to the constructor of an std::function.

这个推理正确吗?如果是这样,当前的实现是否符合要求?

Is this reasoning correct? If so, are current implementations compliant?

推荐答案

C ++ 11§17.6.5.9中指定了Herb谈论的有关标准库对数据竞争安全性的保证:

The behavior that Herb is talking about with respect to the standard library's guarantees on data race safety is specified in C++11 §17.6.5.9:

17.6.5.9 避免数据竞争 [res.on.data.races]

1本节指定实现必须满足的要求,以防止数据争用(1.10).除非另有说明,否则每个标准库函数都应满足每个要求.除以下指定的情况以外,实施可能会阻止数据争用.

1 This section specifies requirements that implementations shall meet to prevent data races (1.10). Every standard library function shall meet each requirement unless otherwise specified. Implementations may prevent data races in cases other than those specified below.

2 C ++标准库函数不得直接或间接访问当前线程以外的线程可访问的对象(1.10),除非通过函数的参数(包括 this )直接或间接访问对象.

2 A C++ standard library function shall not directly or indirectly access objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function’s arguments, including this.

3 C ++标准库函数不得直接或间接修改可被当前线程以外的线程访问的对象(1.10),除非通过函数的非const参数直接或间接访问对象,包括 this .

3 A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const arguments, including this.

4 [注意:例如,这意味着实现无法在没有同步的情况下将静态对象用于内部目的,因为即使在未明确在线程之间共享对象的程序中,它也可能导致数据争用.—尾注]

4 [ Note: This means, for example, that implementations can’t use a static object for internal purposes without synchronization because it could cause a data race even in programs that do not explicitly share objects between threads. —end note ]

5 C ++标准库函数不得访问通过其自变量或其容器自变量的元素间接访问的对象,除非通过对其容器元素的规范调用所需的函数.

5 A C++ standard library function shall not access objects indirectly accessible via its arguments or via elements of its container arguments except by invoking functions required by its specification on those container elements.

6通过调用标准库容器或字符串成员函数获得的迭代器上的操作可以访问基础容器,但不得对其进行修改.[注意:特别是,使迭代器无效的容器操作与对该容器关联的迭代器上的操作发生冲突.——结尾说明]

6 Operations on iterators obtained by calling a standard library container or string member function may access the underlying container, but shall not modify it. [ Note: In particular, container operations that invalidate iterators conflict with operations on iterators associated with that container. —end note ]

7如果对象对用户不可见并且可以防止数据竞争,则实现可以在线程之间共享它们自己的内部对象.

7 Implementations may share their own internal objects between threads if the objects are not visible to users and are protected against data races.

8除非另有说明,否则C ++标准库函数应仅在当前线程内执行所有操作,如果这些操作具有对用户可见(1.10)的效果.

8 Unless otherwise specified, C++ standard library functions shall perform all operations solely within the current thread if those operations have effects that are visible (1.10) to users.

9 [注意:如果没有明显的副作用,这将允许实现并行化操作.—尾注]

9 [ Note: This allows implementations to parallelize operations if there are no visible side effects. —end note ]

假设您通过例如构造函数或赋值运算符将lambda闭包传递给 std :: function -然后调用该 function operator().通过第 1 段,operator () 被允许通过函数的参数直接或间接地访问闭包对象,包括 this".在第2款中,它不能更改 std :: function 对象本身或闭包对象的状态,因为它们都是直接通过函数的非常量参数(包括>此."这种行为很容易实现,而无需采取任何措施防止同时进行的线程访问(即锁定).

Say you pass a lambda closure to std::function - via, e.g., the constructor or assignment operator - and then invoke that function's operator(). By paragraph 1, operator () is allowed to access the closure object "directly or indirectly via the function’s arguments, including this." By paragraph 2, it may not change the state of the std::function object itself or the closure object since they are both "accessed directly or indirectly via the function’s non-const arguments, including this." This behavior is easily achieved without any kind of protection against simultaneous thread accesses, i.e., locking.

operator()调用lambda闭包的 operator(),规则发生变化:您的lambda的 operator()不是标准的库函数,因此不受指定标准库函数行为的规则的约束.您可以根据语言规则对闭包对象执行任何操作.

operator () then invokes the lambda closure's operator(), and the rules change: your lambda's operator () is NOT a standard library function, and hence not subject to the rules that specify the behavior of standard library functions. You can do whatever you like to the closure object subject to the rules of the language.

标准库保证它不会通过其操作引入任何数据争用,但是您应对代码可能引入的任何数据争用负责.

The Standard Library guarantees that it won't introduce any data races through its actions, but you are responsible for any data races that might be introduced by your code.

这篇关于调用内部可变lambda时std :: function是否锁定互斥锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆