为啥 std::unique_lock 改变 std::unique_ptr?

Posted

技术标签:

【中文标题】为啥 std::unique_lock 改变 std::unique_ptr?【英文标题】:Why std::unique_lock changes std::unique_ptr?为什么 std::unique_lock 改变 std::unique_ptr? 【发布时间】:2020-04-14 19:17:19 【问题描述】:

这里发生了一些奇怪的事情,至少对我来说很奇怪......

我有一个线程函数...

void run(std::mutex &mtx, std::condition_variable &cv)

    std::unique_ptr<float[]> shiftbuf(new float[SHIFTBUF_SIZE]);
    float *const shiftbuf_p = shiftbuf.get();

    for (;;)
    
        *(shiftbuf_p + SHIFTBUF_SIZE - 1) = 100.f;
        std::unique_lock<std::mutex> lck(mtx);
        cv.notify_all();
    

主要是...

std::mutex mtx;
std::condition_variable cv;
std::thread th(run, std::ref(mtx), std::ref(cv));

std::unique_lock<std::mutex> lck(mtx);

cv.wait(lck);

当我调试代码并观察shiftbuf_p 时,lck 上的地址更改?!

Thread 18 "..." hit Hardware watchpoint 4: shiftbuf_p

Old value = (float * const) 0x7fffd8000b10
New value = (float * const) 0x7fffd8000b01
run (mtx=..., cv_second=..., amplitude_data=..., tm=...)
    at ....cpp:132
132             std::unique_lock<std::mutex> lck(mtx);
(gdb) c
Continuing.

Thread 18 "..." hit Hardware watchpoint 4: shiftbuf_p

Old value = (float * const) 0x7fffd8000b01
New value = (float * const) 0x7fffd8000001
run (mtx=..., cv_second=..., amplitude_data=..., tm=...)
    at ....cpp:132
132             std::unique_lock<std::mutex> lck(mtx);
(gdb) c
Continuing.

Thread 18 "..." hit Hardware watchpoint 5: shiftbuf_p

Old value = (float * const) 0x7fffd8000001
New value = (float * const) 0x7fff00000001
run (mtx=..., cv_second=..., amplitude_data=..., tm=...)
    at ....cpp:132
132             std::unique_lock<std::mutex> lck(mtx);
(gdb) c
Continuing.

Thread 18 "..." received signal SIGSEGV, Segmentation fault.

而且它永远不会变回到正确的原始地址。 当run() 继续时shiftbuf_p 指向错误的地址。

为什么地址会改变?它是一个常量指针。 unique_ptr 范围在子线程中是顶部的。等待调用在主线程中。

shiftbuf.get() 也会返回nullptr。就像 unique_ptr shiftbuf 超出范围,但这段代码不应该发生这种情况,不是吗?

你能解释一下发生了什么吗?

【问题讨论】:

请提供一个至少可以编译的最小可复制示例 是否启用优化? 可能shiftbuf_p 变量被优化为未使用,而空间被重新用于lck std::condition_variable cv; cv.wait(lck); 这没有意义,您正在等待本地创建的 cv 并且您的线程正在通知其他一些 cv(您没有显示另一个来自哪里) std::unique_ptr&lt;float&gt; shiftbuf(new float[SHIFTBUF_SIZE]); 错误,这里需要使用数组专用的unique_ptrstd::unique_ptr&lt;float[]&gt; shiftbuf(new float[SHIFTBUF_SIZE]); 【参考方案1】:

好的。我想我找到了我的问题。 堆栈上还有另一个数据数组,这个数组被用来写越界,因为它的索引完全失控了。 因此shiftbuf_p 地址可能会更改。 很难找到...没有 -fstack-protector 或 valgrind 检测到原因。

【讨论】:

-fstack-protector 只能防止堆栈 frame 溢出(即当损坏落后于 all 局部变量时),并且只能在返回时。不确定valgrind,但它针对的是堆分配AFAIK。

以上是关于为啥 std::unique_lock 改变 std::unique_ptr?的主要内容,如果未能解决你的问题,请参考以下文章

关于使用 std::unique_lock 的说明

linux C++互斥锁std::lock_guard(轻锁)std::unique_lock(重锁)区别

linux C++互斥锁std::lock_guard(轻锁)std::unique_lock(重锁)区别

在不同的函数中锁定/解锁 std::unique_lock

std::unique_lock 移动语义

C++11 std::unique_lock