nofify_all() 在程序关闭时崩溃
nofify_all() crashes when the program is closed
我有一个非常简单的 C++ 程序,如下所示。 A、B 和 C 类 在 DLL 中。当我关闭这个应用程序时,它有时会在条件变量上调用 notify_all() 时崩溃。谁能告诉我原因吗?
我在 SO 上检查了很多 Q/As,但其中 none 解决了我的问题。我在 windows 7 和 VS2013 上工作。
class B;
class C
{
public:
C(const std::weak_ptr<B>& b) : mB(b)
{
}
virtual ~C()
{
}
void run()
{
while (true)
{
std::unique_lock<std::mutex> uLock(mMutex);
// Wait until some event is happening
mCondition.wait_for(uLock, std::chrono::seconds(300));
if (!mStop)
{
//do something here
}
else
{
return;
}
}
}
void start()
{
mThread = std::thread(&C::run, this);
}
void stop()
{
mStop = false;
}
void notify()
{
mCondition.notify_all();
}
void join()
{
if (mThread.joinable())
{
mThread.join();
}
}
private:
std::atomic<bool> mStop;
std::condition_variable mCondition;
std::mutex mMutex;
std::thread mThread;
std::weak_ptr<B> mB;
};
class B : public std::enable_shared_from_this<B>
{
public:
B() {}
~B()
{
if (mC)
{
mC->stop();
mC->notify();
mC->join();
}
}
// basic methods
void init()
{
mC = std::unique_ptr<C>(new C(shared_from_this()));
mC->start();
}
private:
std::unique_ptr<C> mC;
};
class A
{
public:
~A(){}
void init() { pImpl->init(); }
static std::shared_ptr<A> getInstance(){
static std::shared_ptr<A> instance(new A);
return instance;
}
private:
A() : pImpl(std::make_shared<B>()){}
std::shared_ptr<B> pImpl;
};
void main()
{
std::shared_ptr<A> a = A::getInstance();
a->init();
int x;
std::cin >> x;
}
编辑 1: 如果我将 B 的析构函数中的代码放入不同的函数(例如 clean())并从 main() 调用它(使用 clean() 方法在 A) 中没有崩溃发生。
代码遗漏了条件变量通知,因为:
- 在
stop_ = true
期间不持有互斥量(应该是 true
,而不是 false
)。 stop_
必须在持有互斥量时读取和修改,并且它不需要是原子的。当人们将原子与互斥体和条件变量一起使用时,这是竞争条件的常见原因。
- 条件变量等待代码在等待前不检查条件。
修复:
class B;
class C
{
public:
C(const std::weak_ptr<B>& b) : mB(b) {}
~C() { stop(); }
void run()
{
while (true) {
std::unique_lock<std::mutex> uLock(mMutex);
while(!mStop /* && !other_real_condition */)
mCondition.wait_for(uLock, std::chrono::seconds(300));
if(mStop)
return;
// other_real_condition is true, process it.
}
}
void start()
{
mThread = std::thread(&C::run, this);
}
void stop()
{
{
std::unique_lock<std::mutex> uLock(mMutex);
mStop = true;
}
mCondition.notify_all();
if (mThread.joinable())
mThread.join();
}
private:
bool mStop = false; // <--- do not forget to initialize
std::condition_variable mCondition;
std::mutex mMutex;
std::thread mThread;
std::weak_ptr<B> mB;
};
class B : public std::enable_shared_from_this<B>
{
public:
// basic methods
void init()
{
mC = std::unique_ptr<C>(new C(shared_from_this()));
mC->start();
}
private:
std::unique_ptr<C> mC;
};
如果您设置 mStop
而不持有互斥锁,则会发生以下情况:
| Thread 1 | Thread 2 |
| mStop = true | |
| mCondition.notify_all | |
| | mMutex.lock |
| | mCondition.wait_for |
尽管设置了 mStop
,但上面的线程 2 丢失了通知并等待。
更新共享状态时锁定互斥锁修复了竞争条件:
| Thread 1 | Thread 2 |
| mMutex.lock | |
| mStop = true | |
| mCondition.notify_all | |
| mMutex.unlock | |
| | mMutex.lock |
| | mStop == true, no wait |
当等待条件变量时,必须在持有互斥锁的同时修改和读取共享状态,否则条件通知会丢失,这可能会导致死锁(等待时没有超时)。这就是为什么不需要将原子与互斥锁和条件变量一起使用,您要么使用原子,要么使用互斥锁和条件变量,但不能同时使用两者。
这似乎是一个 CRT 错误 ()。这个问题不会发生在 Windows 的新版本上,即 Windows 10。为了修复 Windows 7 上的崩溃,我只是删除了 condition_variable 并使用了一个简单的睡眠来代替在退出程序时,我只是分离了线程。虽然这不是一个干净的方法,但我认为这是避免崩溃的唯一方法。如果有人找到更好的答案,请告诉我。
我有一个非常简单的 C++ 程序,如下所示。 A、B 和 C 类 在 DLL 中。当我关闭这个应用程序时,它有时会在条件变量上调用 notify_all() 时崩溃。谁能告诉我原因吗?
我在 SO 上检查了很多 Q/As,但其中 none 解决了我的问题。我在 windows 7 和 VS2013 上工作。
class B;
class C
{
public:
C(const std::weak_ptr<B>& b) : mB(b)
{
}
virtual ~C()
{
}
void run()
{
while (true)
{
std::unique_lock<std::mutex> uLock(mMutex);
// Wait until some event is happening
mCondition.wait_for(uLock, std::chrono::seconds(300));
if (!mStop)
{
//do something here
}
else
{
return;
}
}
}
void start()
{
mThread = std::thread(&C::run, this);
}
void stop()
{
mStop = false;
}
void notify()
{
mCondition.notify_all();
}
void join()
{
if (mThread.joinable())
{
mThread.join();
}
}
private:
std::atomic<bool> mStop;
std::condition_variable mCondition;
std::mutex mMutex;
std::thread mThread;
std::weak_ptr<B> mB;
};
class B : public std::enable_shared_from_this<B>
{
public:
B() {}
~B()
{
if (mC)
{
mC->stop();
mC->notify();
mC->join();
}
}
// basic methods
void init()
{
mC = std::unique_ptr<C>(new C(shared_from_this()));
mC->start();
}
private:
std::unique_ptr<C> mC;
};
class A
{
public:
~A(){}
void init() { pImpl->init(); }
static std::shared_ptr<A> getInstance(){
static std::shared_ptr<A> instance(new A);
return instance;
}
private:
A() : pImpl(std::make_shared<B>()){}
std::shared_ptr<B> pImpl;
};
void main()
{
std::shared_ptr<A> a = A::getInstance();
a->init();
int x;
std::cin >> x;
}
编辑 1: 如果我将 B 的析构函数中的代码放入不同的函数(例如 clean())并从 main() 调用它(使用 clean() 方法在 A) 中没有崩溃发生。
代码遗漏了条件变量通知,因为:
- 在
stop_ = true
期间不持有互斥量(应该是true
,而不是false
)。stop_
必须在持有互斥量时读取和修改,并且它不需要是原子的。当人们将原子与互斥体和条件变量一起使用时,这是竞争条件的常见原因。 - 条件变量等待代码在等待前不检查条件。
修复:
class B;
class C
{
public:
C(const std::weak_ptr<B>& b) : mB(b) {}
~C() { stop(); }
void run()
{
while (true) {
std::unique_lock<std::mutex> uLock(mMutex);
while(!mStop /* && !other_real_condition */)
mCondition.wait_for(uLock, std::chrono::seconds(300));
if(mStop)
return;
// other_real_condition is true, process it.
}
}
void start()
{
mThread = std::thread(&C::run, this);
}
void stop()
{
{
std::unique_lock<std::mutex> uLock(mMutex);
mStop = true;
}
mCondition.notify_all();
if (mThread.joinable())
mThread.join();
}
private:
bool mStop = false; // <--- do not forget to initialize
std::condition_variable mCondition;
std::mutex mMutex;
std::thread mThread;
std::weak_ptr<B> mB;
};
class B : public std::enable_shared_from_this<B>
{
public:
// basic methods
void init()
{
mC = std::unique_ptr<C>(new C(shared_from_this()));
mC->start();
}
private:
std::unique_ptr<C> mC;
};
如果您设置 mStop
而不持有互斥锁,则会发生以下情况:
| Thread 1 | Thread 2 |
| mStop = true | |
| mCondition.notify_all | |
| | mMutex.lock |
| | mCondition.wait_for |
尽管设置了 mStop
,但上面的线程 2 丢失了通知并等待。
更新共享状态时锁定互斥锁修复了竞争条件:
| Thread 1 | Thread 2 |
| mMutex.lock | |
| mStop = true | |
| mCondition.notify_all | |
| mMutex.unlock | |
| | mMutex.lock |
| | mStop == true, no wait |
当等待条件变量时,必须在持有互斥锁的同时修改和读取共享状态,否则条件通知会丢失,这可能会导致死锁(等待时没有超时)。这就是为什么不需要将原子与互斥锁和条件变量一起使用,您要么使用原子,要么使用互斥锁和条件变量,但不能同时使用两者。
这似乎是一个 CRT 错误 ()。这个问题不会发生在 Windows 的新版本上,即 Windows 10。为了修复 Windows 7 上的崩溃,我只是删除了 condition_variable 并使用了一个简单的睡眠来代替在退出程序时,我只是分离了线程。虽然这不是一个干净的方法,但我认为这是避免崩溃的唯一方法。如果有人找到更好的答案,请告诉我。