我的等待 - 使用std :: mutex的通知机制是否正确?

时间:2017-04-28 08:51:18

标签: c++ multithreading c++11 stdmutex

我开始使用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中实现它的正确方法是什么?

4 个答案:

答案 0 :(得分:4)

听起来你正在寻找condition variable。最后总是应该有一种方法让它通过互斥体工作,但条件变量是当前C ++惯用的方法来处理`阻塞并等到事情发生''场景。

答案 1 :(得分:2)

当持有它的线程试图锁定它时,互斥锁的行为是未定义的。当未挂起它的线程尝试解锁时,互斥锁的行为是未定义的。因此,您的代码可以在各种平台上执行任何操作。

相反,将mutex与条件变量和谓词布尔值一起使用。在伪代码中:

阻止:

  1. 获取互斥锁。

  2. 谓词为false时,阻止条件变量。

  3. 如果您想在这里重新武装,请将谓词设置为false。

  4. 释放互斥锁。

  5. 要发布:

    1. 获取互斥锁。

    2. 将谓词设置为true。

    3. 发出条件变量信号。

    4. 释放互斥锁。

    5. 重新安装:

      1. 获取互斥锁。

      2. 将谓词设置为false。

      3. 释放互斥锁。

答案 2 :(得分:2)

请检查此代码....

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();
}

我希望你能得到解决方案。这是非常合适的!!

答案 3 :(得分:2)

代码

的问题
// Ensures the mutex will be locked
while(myWaitMutex.try_lock());

.try_lock()尝试获取锁定并返回true如果成功,,代码显示&#34; 如果我们获取锁定然后重试一次又一次地锁定它直到我们失败&#34;。我们永远不会失败&#34;因为我们现在拥有自己的锁,我们正在等待,所以这将是一个无限循环。此外,尝试使用调用者已经获得锁定的std::mutex进行锁定是UB,因此保证为UB。如果不成功,.try_lock()将返回false,并退出while循环。换句话说,这将确保锁定互斥锁。

确保互斥锁被锁定的正确方法是:

myWaitMutex.lock();

这将导致当前线程阻塞(无限期),直到它可以获取锁定。

接下来,另一个线程尝试解锁一个互斥锁 没有锁定。

// Executed when thread 1 should resume processing:
myWaitMutex.unlock();

这不会对你.unlock() std::mutex上的std::mutex工作,但你还没有锁定。

使用锁

使用互斥锁时,更容易使用RAII所有权包装对象,例如std::lock_guardstd::lock_guard的使用模式始终为:&#34; 锁定 - &gt;在关键部分做一些事情 - &gt;解锁&#34 ;. 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

简单锁定可能不是作业的最佳工具

如果你想要的是能够发出线程唤醒信号,那么使用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. } 允许任何调用者向等待的线程发送信号,而无需持有任何锁

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();
}

然而,为了进一步复杂化,这个叫做虚假唤醒的东西意味着任何等待的线程都可能因为未知原因而随时醒来。这在大多数系统中都是事实,并且与线程调度的内部工作有关。此外,我们可能需要检查是否真的需要等待,因为我们正在处理并发。例如,如果通知线程在之前通知我们开始等待,那么我们可能永远等待,除非我们有办法首先检查这个。

为了解决这个问题,我们需要添加一个while循环和一个谓词,告诉我们什么时候需要等待以及我们何时等待。

Spinner spinnerJobTypes = FindViewById<Spinner>(Resource.Id.spinnerJobTypes);
spinnerJobTypes.ItemSelected += new EventHandler<AdapterView.ItemSelectedEventArgs> (spinner_ItemSelected);

private void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
    {
            Spinner spinner = (Spinner)sender;

            string toast = string.Format ("The planet is {0}", spinner.GetItemAtPosition (e.Position));
            Toast.MakeText (this, toast, ToastLength.Long).Show ();
    }

还有其他原因,为什么在循环中等待并使用诸如“被盗唤醒”之类的谓词是一个好主意。正如@ David Schwartz的评论中提到的那样。