我发现这段代码无法正常工作:
#pragma pack(push, 1)
class myclass {
protected:
bool mycrasher[1]; // with mycrasher[4] it works!
std::mutex mtx;
std::condition_variable cv;
void thread_func() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::chrono::milliseconds timeout(1000);
std::unique_lock<std::mutex> l(mtx, std::defer_lock);
while (true) {
auto now = std::chrono::system_clock::now();
l.lock();
while (true) {
std::cout << "Waiting..." << std::endl;
auto result = cv.wait_until(l, now + timeout);
std::cout << "Timed out..." << std::endl;
if (result == std::cv_status::timeout)
break;
}
l.unlock();
}
}
public:
myclass() {
std::lock_guard<std::mutex> l(mtx);
std::thread *t = new std::thread(&myclass::thread_func, this);
t->detach();
};
void start() {
std::cout << "myclass started." << std::endl;
std::cout << "sizeof(std::mutex) = " << sizeof(std::mutex) << std::endl;
std::cout << "sizeof(std::condition_variable) = " << sizeof(std::condition_variable) << std::endl;
}
};
#pragma pack(pop)
int main() {
myclass x;
x.start();
std::this_thread::sleep_for(std::chrono::seconds(60));
}
我希望代码在cv.wait_until
通话中等待一秒钟,然后重复,但只是在通话中挂起。如果我删除#pragma
指令,问题(直观地)就会消失,因为我正在打包互斥锁和CV。但是,当我运行此代码时,我得到:
myclass started.
sizeof(std::mutex) = 40
sizeof(std::condition_variable) = 48
有或没有pragma
,所以包装似乎不是真正的问题。
此外,我发现如果我将mycrasher
变量与4字节边界对齐,问题也会消失。同样,如果我在std::condition_variable cv
声明后移动变量,问题就会消失,但在std::mutex mtx
和std::condition_variable cv
之间移动时,它仍然存在。
当CV未正确对齐时,为什么代码段会在cv.wait_until
来电时挂起?预计性能会受到影响,但不是普通的失速。
在Debian 8系统上用g ++ 4.9.2和g ++ 6.3重现。
答案 0 :(得分:1)
不要使用包装,你不应该对具有互斥和条件变量的结构这样做。如果您认为自己确实需要,那么您的方法就会出现问题,因为您通常不应该创建许多这些对象,并且不应该在那里打包。
作为一种简单的解决方法,请将mycrasher
放在mtx
和cv
下方
#pragma pack(push, 1)
class myclass
{
protected:
std::mutex mtx;
std::condition_variable cv;
bool mycrasher[1];
void thread_func();
public:
myclass();
void start();
};
#pragma pack(pop)
由于mtx
和cv
最终会出现奇数地址而某些使用这些内部代码的内部代码不同,因此它最有可能在启用打包的情况下无法正常工作期望。在某些平台上(例如ARM)可能会崩溃。
作为旁注,您的代码不仅存在对齐问题,还会泄漏myclass
构造函数内的堆上的线程对象。