我的等待 - 使用 std::mutex 的通知机制是否正确?
Is my wait - notify mechanism using std::mutex correct?
我开始使用 std::mutexes 来停止一个线程并等待另一个线程恢复它。它是这样工作的:
线程 1
// Ensures the mutex will be locked
while(myWaitMutex.try_lock());
// Locks it again to pause this thread
myWaitMutex.lock();
线程 2
// Executed when thread 1 should resume processing:
myWaitMutex.unlock();
但是我不确定这是否正确以及是否可以在所有平台上正常运行。如果这不正确,在 C++11 中实现它的正确方法是什么?
当持有它的线程试图锁定它时,互斥体的行为是未定义的。当不持有互斥锁的线程试图解锁它时,互斥锁的行为是未定义的。因此,您的代码可能会在各种平台上执行任何操作。
相反,将互斥量与条件变量和谓词布尔值一起使用。在伪代码中:
要阻止:
获取互斥量。
当谓词为假时,阻塞条件变量。
如果你想在这里重新武装,把predicate设置为false。
释放互斥。
要发布:
获取互斥锁。
将谓词设置为真。
通知条件变量。
释放互斥。
重新武装:
获取互斥锁。
将谓词设置为 false。
释放互斥。
听起来您正在寻找 condition variable。最后应该总是有办法让它通过互斥锁工作,但条件变量是当前 C++ 处理“阻塞并等待某事发生”场景的惯用方式。
请检查此代码....
std::mutex m_mutex;
std::condition_variable m_cond_var;
void threadOne(){
std::unique_lock<std::mutex> lck(mtx);
while (!ready){
m_cond_var.wait(lck);
}
m_cond_var.notify_all();
}
void threadTwo(){
std::unique_lock<std::mutex> lck(mtx);
read = true;
m_cond_var.notify_all();
}
希望您能找到解决方案。而且非常合适!!
代码问题
// Ensures the mutex will be locked
while(myWaitMutex.try_lock());
.try_lock()
尝试获取锁,如果成功,returns true
,即,代码显示“如果我们获得锁,然后一次又一次地重试锁定它,直到我们失败”。我们永远不能 "fail" 因为我们目前拥有我们正在等待的锁,所以这将是一个无限循环。此外,尝试使用调用者已经获得锁定的 std::mutex
进行锁定是 UB,因此这保证是 UB。如果不成功,.try_lock()
将 return false
并退出 while
循环。换句话说,这将 而不是 确保互斥体被锁定。
确保互斥锁被锁定的正确方法很简单:
myWaitMutex.lock();
这将导致当前线程阻塞(无限期地)直到它可以获取锁。
接下来,另一个线程尝试解锁它没有锁定的互斥锁。
// Executed when thread 1 should resume processing:
myWaitMutex.unlock();
这不会起作用,因为它是 .unlock()
在 std::mutex
上的 UB,您还没有锁定。
使用锁
使用互斥锁时,使用 RAII 所有权包装对象(例如 std::lock_guard
会更容易。 std::mutex
的使用模式总是:“锁定 -> 在临界区做一些事情 -> 解锁 ”。 std::lock_guard
将在其构造函数中锁定互斥锁,并在其析构函数中将其解锁。不用担心什么时候上锁解锁之类的底层问题
std::mutex m;
{
std::lock_guard<std::mutex> lk{m};
/* We have the lock until we exit scope. */
} // Here 'lk' is destroyed and will release lock.
一把简单的锁可能不是完成这项工作的最佳工具
如果您想要的是能够通知线程唤醒,那么可以使用 std::condition_variable
的 wait 和 notify 结构。 std::condition_variable
允许任何调用者向等待线程发送信号 而无需持有任何锁 .
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std::literals;
int main() {
std::mutex m;
std::condition_variable cond;
std::thread t{[&] {
std::cout << "Entering sleep..." << std::endl;
std::unique_lock<std::mutex> lk{m};
cond.wait(lk); // Will block until 'cond' is notified.
std::cout << "Thread is awake!" << std::endl;
}};
std::this_thread::sleep_for(3s);
cond.notify_all(); // Notify all waiting threads.
t.join(); // Remember to join thread before exit.
}
然而,为了使事情更加复杂,有一个叫做 spurious wakeups 的东西,这意味着任何等待的线程可能会在任何时间出于未知原因被唤醒。这是大多数系统的事实,并且与线程调度的内部工作有关。此外,我们可能需要检查在处理并发时是否确实需要等待。例如,如果通知线程碰巧在之前我们开始等待,那么我们可能永远等待除非我们有办法先检查这个.
为了处理这个问题,我们需要添加一个 while 循环和一个判断何时需要等待以及何时完成等待的谓词。
int main() {
std::mutex m;
std::condition_variable cond;
bool done = false; // Flag for indicating when done waiting.
std::thread t{[&] {
std::cout << "Entering sleep..." << std::endl;
std::unique_lock<std::mutex> lk{m};
while (!done) { // Wait inside loop to handle spurious wakeups etc.
cond.wait(lk);
}
std::cout << "Thread is awake!" << std::endl;
}};
std::this_thread::sleep_for(3s);
{ // Aquire lock to avoid data race on 'done'.
std::lock_guard<std::mutex> lk{m};
done = true; // Set 'done' to true before notifying.
}
cond.notify_all();
t.join();
}
在@.
的评论中提到,在循环中等待并使用诸如 "stolen wakeups" 之类的谓词是个好主意还有其他原因
我开始使用 std::mutexes 来停止一个线程并等待另一个线程恢复它。它是这样工作的:
线程 1
// Ensures the mutex will be locked
while(myWaitMutex.try_lock());
// Locks it again to pause this thread
myWaitMutex.lock();
线程 2
// Executed when thread 1 should resume processing:
myWaitMutex.unlock();
但是我不确定这是否正确以及是否可以在所有平台上正常运行。如果这不正确,在 C++11 中实现它的正确方法是什么?
当持有它的线程试图锁定它时,互斥体的行为是未定义的。当不持有互斥锁的线程试图解锁它时,互斥锁的行为是未定义的。因此,您的代码可能会在各种平台上执行任何操作。
相反,将互斥量与条件变量和谓词布尔值一起使用。在伪代码中:
要阻止:
获取互斥量。
当谓词为假时,阻塞条件变量。
如果你想在这里重新武装,把predicate设置为false。
释放互斥。
要发布:
获取互斥锁。
将谓词设置为真。
通知条件变量。
释放互斥。
重新武装:
获取互斥锁。
将谓词设置为 false。
释放互斥。
听起来您正在寻找 condition variable。最后应该总是有办法让它通过互斥锁工作,但条件变量是当前 C++ 处理“阻塞并等待某事发生”场景的惯用方式。
请检查此代码....
std::mutex m_mutex;
std::condition_variable m_cond_var;
void threadOne(){
std::unique_lock<std::mutex> lck(mtx);
while (!ready){
m_cond_var.wait(lck);
}
m_cond_var.notify_all();
}
void threadTwo(){
std::unique_lock<std::mutex> lck(mtx);
read = true;
m_cond_var.notify_all();
}
希望您能找到解决方案。而且非常合适!!
代码问题
// Ensures the mutex will be locked
while(myWaitMutex.try_lock());
.try_lock()
尝试获取锁,如果成功,returns true
,即,代码显示“如果我们获得锁,然后一次又一次地重试锁定它,直到我们失败”。我们永远不能 "fail" 因为我们目前拥有我们正在等待的锁,所以这将是一个无限循环。此外,尝试使用调用者已经获得锁定的 std::mutex
进行锁定是 UB,因此这保证是 UB。如果不成功,.try_lock()
将 return false
并退出 while
循环。换句话说,这将 而不是 确保互斥体被锁定。
确保互斥锁被锁定的正确方法很简单:
myWaitMutex.lock();
这将导致当前线程阻塞(无限期地)直到它可以获取锁。
接下来,另一个线程尝试解锁它没有锁定的互斥锁。
// Executed when thread 1 should resume processing:
myWaitMutex.unlock();
这不会起作用,因为它是 .unlock()
在 std::mutex
上的 UB,您还没有锁定。
使用锁
使用互斥锁时,使用 RAII 所有权包装对象(例如 std::lock_guard
会更容易。 std::mutex
的使用模式总是:“锁定 -> 在临界区做一些事情 -> 解锁 ”。 std::lock_guard
将在其构造函数中锁定互斥锁,并在其析构函数中将其解锁。不用担心什么时候上锁解锁之类的底层问题
std::mutex m;
{
std::lock_guard<std::mutex> lk{m};
/* We have the lock until we exit scope. */
} // Here 'lk' is destroyed and will release lock.
一把简单的锁可能不是完成这项工作的最佳工具
如果您想要的是能够通知线程唤醒,那么可以使用 std::condition_variable
的 wait 和 notify 结构。 std::condition_variable
允许任何调用者向等待线程发送信号 而无需持有任何锁 .
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std::literals;
int main() {
std::mutex m;
std::condition_variable cond;
std::thread t{[&] {
std::cout << "Entering sleep..." << std::endl;
std::unique_lock<std::mutex> lk{m};
cond.wait(lk); // Will block until 'cond' is notified.
std::cout << "Thread is awake!" << std::endl;
}};
std::this_thread::sleep_for(3s);
cond.notify_all(); // Notify all waiting threads.
t.join(); // Remember to join thread before exit.
}
然而,为了使事情更加复杂,有一个叫做 spurious wakeups 的东西,这意味着任何等待的线程可能会在任何时间出于未知原因被唤醒。这是大多数系统的事实,并且与线程调度的内部工作有关。此外,我们可能需要检查在处理并发时是否确实需要等待。例如,如果通知线程碰巧在之前我们开始等待,那么我们可能永远等待除非我们有办法先检查这个.
为了处理这个问题,我们需要添加一个 while 循环和一个判断何时需要等待以及何时完成等待的谓词。
int main() {
std::mutex m;
std::condition_variable cond;
bool done = false; // Flag for indicating when done waiting.
std::thread t{[&] {
std::cout << "Entering sleep..." << std::endl;
std::unique_lock<std::mutex> lk{m};
while (!done) { // Wait inside loop to handle spurious wakeups etc.
cond.wait(lk);
}
std::cout << "Thread is awake!" << std::endl;
}};
std::this_thread::sleep_for(3s);
{ // Aquire lock to avoid data race on 'done'.
std::lock_guard<std::mutex> lk{m};
done = true; // Set 'done' to true before notifying.
}
cond.notify_all();
t.join();
}
在@