bool compare_exchange_weak (T& expected, T val, ..);
Run Code Online (Sandbox Code Playgroud)
compare_exchange_weak()是C++ 11中提供的比较交换原语之一.它在某种意义上很弱,即使对象的值等于它也会返回false expected.这是由于某些平台上的虚假故障导致一系列指令(而不是x86上的指令)用于实现它.在这样的平台上,上下文切换,由另一个线程重新加载相同的地址(或高速缓存行)等可能使原语失败.这是spurious因为它不是expected操作失败的对象(不等于)的值.相反,这是一种时间问题.
但让我感到困惑的是C++ 11标准(ISO/IEC 14882)中所说的,
29.6.5 ..虚假失败的后果是几乎所有弱比较和交换的使用都将处于循环中.
为什么几乎所有用途都必须处于循环中?这是否意味着我们会因为虚假失败而失败?如果是这样的话,为什么我们compare_exchange_weak()自己打扰使用并编写循环?我们可以使用compare_exchange_strong()我认为应该摆脱我们的虚假失败.有哪些常见用例compare_exchange_weak()?
另一个问题有关.安东尼在他的书"C++ Concurrency In Action"中说,
//Because compare_exchange_weak() can fail spuriously, it must typically
//be used in a loop:
bool expected=false;
extern atomic<bool> b; // set somewhere else
while(!b.compare_exchange_weak(expected,true) && !expected);
//In this case, you keep looping as long as expected is still false,
//indicating that the compare_exchange_weak() …Run Code Online (Sandbox Code Playgroud) 在我的项目中,我们有一段这样的代码:
// raw data consists of 4 ints
unsigned char data[16];
int i1, i2, i3, i4;
i1 = *((int*)data);
i2 = *((int*)(data + 4));
i3 = *((int*)(data + 8));
i4 = *((int*)(data + 12));
Run Code Online (Sandbox Code Playgroud)
我和我的技术负责人谈到这个代码可能不便携,因为它试图将a unsigned char*转换为int*通常具有更严格的对齐要求的代码.但是技术主管说没关系,大多数编译器在转换后仍保持相同的指针值,我可以像这样编写代码.
坦率地说,我并不是真的相信.经过研究,我发现有些人反对使用上面的指针铸件,例如,这里和这里.
所以这是我的问题:
reinterpret_cast?之间有什么区别吗?我知道C99之前的C标准(以及C++)说堆栈上的数组大小必须在编译时知道.但那是为什么呢?堆栈上的数组在运行时分配.那么为什么大小在编译时很重要?希望有人向我解释编译器在编译时将如何处理大小.谢谢.
这种数组的例子是:
void func()
{
/*Here "array" is a local variable on stack, its space is allocated
*at run-time. Why does the compiler need know its size at compile-time?
*/
int array[10];
}
Run Code Online (Sandbox Code Playgroud) 在我看来,定义总是意味着存储分配.
在以下代码中,int i在程序堆栈上分配一个4字节(通常)存储并将其绑定到i,i = 3并将3分配给该存储.但是因为goto,定义被绕过,这意味着没有分配存储空间i.
我听说局部变量在函数的入口处(f()在这种情况下)分配在它们所在的位置,或者在定义点处.
但无论哪种方式,如何i在尚未定义的情况下使用它(根本没有存储空间)?执行时分配给值3的位置在哪里i = 3?
void f()
{
goto label;
int i;
label:
i = 3;
cout << i << endl; //prints 3 successfully
}
Run Code Online (Sandbox Code Playgroud) 可能重复:是否
会使用goto泄漏变量?
在下面的示例中,当goto调用"向后"时,A调用析构函数.为什么会那样?对象a不会离开它的范围,是吗?标准是否说明了这种行为goto?
void f()
{
start:
A a;
goto start;
}
Run Code Online (Sandbox Code Playgroud) 现代CPU架构通常采用可导致无序执行的性能优化,这是很常见的.在单线程应用程序中,也可能发生内存重新排序,但它对程序员来说是不可见的,就好像按程序顺序访问内存一样.对于SMP来说,内存障碍可以用来强制执行某种内存排序.
我不确定的是关于单处理器中的多线程.请考虑以下示例:当线程1运行时,商店f可以在商店之前发生x.假设在f写入之后以及之前x写入上下文切换.现在线程2开始运行,它结束循环并打印0,这当然是不可取的.
// Both x, f are initialized w/ 0.
// Thread 1
x = 42;
f = 1;
// Thread 2
while (f == 0)
;
print x;
Run Code Online (Sandbox Code Playgroud)
上述情况可能吗?或者是否保证在线程上下文切换期间提交物理内存?
根据这个维基,
当程序在单CPU机器上运行时,硬件执行必要的簿记,以确保程序执行就好像所有内存操作都按程序员指定的顺序执行(程序顺序),因此不需要内存屏障.
虽然它没有明确提到单处理器多线程应用程序,但它包括这种情况.
我不确定它是否正确/完整.请注意,这可能高度依赖于硬件(弱/强内存模型).因此,您可能希望在答案中包含您知道的硬件.谢谢.
PS.设备I/O等不是我关心的问题.它是一个单核单处理器.
编辑:感谢Nitsan的提醒,我们假设这里没有编译器重新排序(只是硬件重新排序),并且线程2中的循环没有被优化掉.再次,魔鬼在细节中.
Meyers在他的书"Effective C++"中提到,在某些情况下,非成员非朋友函数比成员函数更好地封装.
例:
// Web browser allows to clear something
class WebBrowser {
public:
...
void clearCache();
void clearHistory();
void removeCookies();
...
};
Run Code Online (Sandbox Code Playgroud)
许多用户希望一起执行所有这些操作,因此WebBrowser也可能提供一个功能:
class WebBrowser {
public:
...
void clearEverything(); // calls clearCache, clearHistory, removeCookies
...
};
Run Code Online (Sandbox Code Playgroud)
另一种方法是定义非成员非朋友函数.
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.removeCookies();
}
Run Code Online (Sandbox Code Playgroud)
非成员函数更好,因为"它不会增加可以访问类的私有部分的函数的数量.",从而导致更好的封装.
喜欢的功能clearBrowser是方便的功能,因为他们不能提供任何功能的WebBrowser一些其他方式的客户端无法获得已.例如,如果clearBrowser不存在,客户可以只打电话clearCache,clearHistory和removeCookies他们自己.
对我而言,便利功能的例子是合理的.但是当非会员版本擅长时,除了便利功能之外还有其他例子吗?
更一般地说,什么时候使用哪些规则?
我注意到不允许创建零堆长度的非堆分配数组.
// error: cannot allocate an array of constant length zero
char a[0];
Run Code Online (Sandbox Code Playgroud)
我还注意到它允许创建零长度的堆分配数组.
// this is okay though
char *pa = new char[0];
Run Code Online (Sandbox Code Playgroud)
我猜他们都是标准保证(我手头没有标准副本).如果是这样,为什么他们如此不同?为什么不在堆栈上允许零长度数组(反之亦然)?
我知道有些CPU架构不支持未对齐的地址访问(例如,ARM 4之前的ARM架构没有访问内存中半字对象的指令).并且该架构的某些编译器(例如,某些版本的GCC)在找到未对齐的地址时将使用一系列内存访问,因此未对齐的访问对开发人员来说几乎是透明的.(请参阅GCC的权威指南,William冯哈根)
但我想知道编译器如何知道地址是否对齐?毕竟,编译器看到的是虚拟地址(有效地址,EA),如果它可以看到任何东西.程序运行时,EA可以通过OS映射到任何物理地址.即使虚拟地址对齐,生成的物理地址也可能未对齐,不是吗?物理地址的对齐是真正重要的,并在CPU地址线上传输.
因为编译器根本不知道物理地址,所以如何知道变量的地址是否对齐?
下面的示例说明了如何防止复制派生类.它基于一个基类,其中声明了复制构造函数和复制赋值运算符private.
class Uncopyable
{
protected:
// allow construction and destruction of derived objects...
Uncopyable() {}
~Uncopyable() {}
private:
// but prevent copying...
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
Run Code Online (Sandbox Code Playgroud)
我们可以使用这个类,结合私有继承,使类不可复制:
class derived: private Uncopyable
{...};
Run Code Online (Sandbox Code Playgroud)
请注意,类中的析构函数Uncopyable未声明为virtual.
以前,我了解到了
virtual.virtual.在这个例子中,析构函数Uncopyable不是virtual,但它是继承的.这似乎违背了我之前学到的智慧.
何时以及为什么基类中的析构函数不能被定义为virtual?