本文介绍了像GCC这样的编译器如何实现std :: mutex的获取/释放语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的理解是std :: mutex锁定和解锁具有获取/释放语义,这将防止它们之间的指令被移出外部.

My understanding is that std::mutex lock and unlock have a acquire/release semantics which will prevent instructions between them from being moved outside.

因此,获取/释放应该同时禁用编译器和CPU重新排序指令.

So acquire/release should disable both compiler and CPU reorder instructions.

我的问题是,我看一下GCC5.1代码库,在std :: mutex :: lock/unlock中看不到任何特殊内容,以防止编译器重新排序代码.

My question is that I take a look at GCC5.1 code base and don't see anything special in std::mutex::lock/unlock to prevent compiler reordering codes.

我在> does-pthread-mutex-lock中找到了可能的答案-have-happens-before-semantics 表示邮件充当了编译器的内存屏障.

I find a potential answer in does-pthread-mutex-lock-have-happens-before-semantics which indicates a mail that says a external function call act as compiler memory fences.

这总是对的吗?标准在哪里?

Is it always true? And where is the standard?

推荐答案

所有这些问题都源于编译器重新排序的规则.重新排序的基本规则之一是,编译器必须证明,重新排序不会改变程序的结果.在std::mutex的情况下,该短语的确切含义在法拉利语的大约10个 pages 块中指定,但通常的直觉意义是不会更改程序的结果"持有.如果您可以保证首先执行哪个操作,则根据规范,不允许编译器以违反该保证的方式重新排序.

All of these questions stem from the rules for compiler reordering. One of the fundamental rules for reordering is that the compiler must prove that the reorder does not change the result of the program. In the case of std::mutex, the exact meaning of that phrase is specified in a block of about 10 pages of legaleese, but the general intuitive sense of "doesn't change the result of the program" holds. If you had a guarantee about which operation came first, according to the specification, no compiler is allowed to reorder in a way which violates that guarantee.

这就是为什么人们经常声称函数调用充当存储障碍"的原因.如果编译器无法深入检查该函数,则无法证明该函数内部没有隐藏的障碍或原子操作,因此必须将该函数视为障碍.

This is why people often claim that a "function call acts as a memory barrier." If the compiler cannot deep-inspect the function, it cannot prove that the function didn't have a hidden barrier or atomic operation inside of it, thus it must treat that function as though it was a barrier.

当然,编译器可以检查函数,例如内联函数或链接时间优化.在这种情况下,不能依赖函数调用来充当障碍,因为编译器可能确实有足够的信息来证明重写行为与原始行为相同.

There is, of course, the case where the compiler can inspect the function, such as the case of inline functions or link time optimizations. In these cases, one cannot rely on a function call to act as a barrier, because the compiler may indeed have enough information to prove the rewrite behaves the same as the original.

对于互斥锁,即使进行这种高级优化也无法进行.在互斥锁/解锁函数调用周围重新排序的唯一方法是对功能进行深入检查,并证明没有障碍或原子操作需要处理.如果它不能检查该锁定/解锁功能的每个子调用和子子调用,则无法证明重新排序是安全的.如果确实可以执行此检查,则会看到每个互斥量实现都包含一些无法重新排序的内容(实际上,这是有效互斥量实现的定义的一部分).因此,即使在这种极端情况下,仍然禁止编译器进行优化.

In the case of mutexes, even such advanced optimization cannot take place. The only way to reorder around the mutex lock/unlock function calls is to have deep-inspected the functions and proven there are no barriers or atomic operations to deal with. If it can't inspect every sub-call and sub-sub-call of that lock/unlock function, it can't prove it is safe to reorder. If it indeed can do this inspection, it would see that every mutex implementation contains something which cannot be reordered around (indeed, this is part of the definition of a valid mutex implementation). Thus, even in that extreme case, the compiler is still forbidden from optimizing.

编辑:为完整起见,我想指出这些规则是C ++ 11中引入的. C ++ 98和C ++ 03重新排序规则仅禁止影响当前线程结果的更改.这样的保证还不足以开发诸如互斥锁之类的多线程基元.

EDIT: For completeness, I would like to point out that these rules were introduced in C++11. C++98 and C++03 reordering rules only prohibited changes that affected the result of the current thread. Such a guarantee is not strong enough to develop multithreading primitives like mutexes.

为解决这个问题,像pthreads这样的多线程API制定了自己的规则.从 Pthreads规范第4.11节:

To deal with this, multithreading APIs like pthreads developed their own rules. from the Pthreads specification section 4.11:

然后列出了几十个用于同步内存的函数,包括pthread_mutex_lockpthread_mutex_unlock.

It then lists a few dozen functions which synchronize memory, including pthread_mutex_lock and pthread_mutex_unlock.

一个希望支持pthreads库的编译器必须实现一些东西来支持这种跨线程内存同步,即使C ++规范对此没有说明.幸运的是,要进行多线程处理的任何编译器都是在开发人员认识到这样的保证对所有多线程处理都是根本的基础上开发出来的,因此每个支持多线程处理的编译器都具有这种保证!

A compiler which wishes to support the pthreads library must implement something to support this cross-thread memory synchronization, even though the C++ specification didn't say anything about it. Fortunately, any compiler where you want to do multithreading was developed with the recognition that such guarantees are fundamental to all multithreading, so every compiler that supports multithreading has it!

在使用gcc的情况下,它在pthreads函数调用上没有任何特殊说明,因为gcc会有效围绕每个外部函数调用创建一个障碍(因为它不能证明没有同步)存在于该函数调用中).如果要更改gcc,则还必须更改其pthreads标头,以包括将pthreads功能标记为同步内存所需的任何额外的措辞.

In the case of gcc, it did so without any special notes on the pthreads function calls because gcc would effectively create a barrier around every external function call (because it couldn't prove that no synchronization existed inside that function call). If gcc were to ever change that, they would also have to change their pthreads headers to include any extra verbage needed to mark the pthreads functions as synchronizing memory.

当然,所有这些都是编译器特定的.在C ++ 11推出新的内存模型之前,没有标准的答案可以解决这个问题.

All of that, of course, is compiler specific. There were no standards answers to this question until C++11 came along with its new memory model.

这篇关于像GCC这样的编译器如何实现std :: mutex的获取/释放语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-19 13:28