【发布时间】:2015-03-22 14:18:24
【问题描述】:
在下面的代码中,while ( !Ref.expired() ); 被愉快地优化成一个无限循环。如果代码行改为while ( !Ref.lock() );。一切都按预期工作。所以真的有两个问题:
1) 当std::weak_ptr::expired() 访问内存围栏计数器时,编译器如何优化过期?
2) Ref.lock() 真的安全吗,或者这也可以优化掉?
下面的示例代码。
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
class A
{
public:
A()
{
m_SomePtr = std::make_shared<bool>( false );
}
virtual ~A()
{
std::weak_ptr<bool> Ref = m_SomePtr;
m_SomePtr.reset();
// Spin (will be optimised into an infinite loop in release builds)
while ( !Ref.expired() );
}
std::shared_ptr<bool> GetPtr() const { return m_SomePtr; }
private:
std::shared_ptr<bool> m_SomePtr;
};
class B
{
public:
B( std::shared_ptr<bool> SomePtr ) : m_Ref( SomePtr ) {}
void LockPtr() { m_SomePtr = m_Ref.lock(); }
void UnLockPtr() { m_SomePtr.reset(); }
private:
std::shared_ptr<bool> m_SomePtr;
std::weak_ptr<bool> m_Ref;
};
int main()
{
std::unique_ptr<A> a( new A() );
std::unique_ptr<B> b( new B( a->GetPtr() ) );
b->LockPtr();
std::cout << "Starting " << std::endl;
std::thread first( [&]()
{
std::this_thread::sleep_for( std::chrono::seconds( 5 ) );
b->UnLockPtr();
} );
std::thread second( [&]()
{
a.reset( nullptr );
} );
first.join();
second.join();
std::cout << "Complete" << std::endl;
return 0;
}
【问题讨论】:
-
为 joyfully 优化点赞。你是在建议机器和/或程序可以玩得开心吗?
-
@Walter 我有一个明显的印象,即编译器对自己非常满意。然而,我非常不高兴。编译器和我关系不和。
-
@PuffOfHotAir:那个编译器和大家的关系很混乱。
-
是什么让您产生了柜台被围起来的想法?我在标准中找不到任何说明它的内容,并且 gcc 的 libstdc++ 在这里与 msvc 一致,因为它对计数器使用宽松的内存顺序(即使 gcc 默认不再进行循环优化;尝试使用
-faggressive-loop-optimizations)。另外,按照我的阅读方式,在weak_ptr上调用lock()会引入数据竞争。 -
@Wintermute 我没有标准,但link 似乎讨论了关于
lock()的歧义,可能导致executed atomically。关键是从线程安全的角度来看,代码正确(expired()必须访问线程安全计数器),但优化器删除了留下非功能代码的调用。跨度>
标签: c++ multithreading c++11 visual-c++ shared-ptr