C ++ 14线程/条件变量误解

时间:2017-07-02 12:00:54

标签: c++ multithreading

我正在尝试使用类成员运行一个带有函数的线程,并使用条件变量等待主线程发出信号并添加线程发出信号的时间。这是代码:

// Example program
#include <iostream>
#include <string>
#include <atomic>
#include <thread>
#include <unistd.h>
#include <mutex>
#include <condition_variable>

std::mutex m_mutex;
std::condition_variable m_condVar;
char stop =0;

class dummclass
{
    std::thread dummclass_thread;
    int alarms;

public:
    dummclass() : 
        alarms(0),
        dummclass_thread(std::thread(&dummclass::dummclassThreadProc, this))
    {
    }

    ~dummclass()
    {
        std::cout<<"Alarms: "<<alarms<<"\n";
        //signal thread before joining
        {
            std::lock_guard<std::mutex> lock_guard(m_mutex);
            stop=1;
        }
        m_condVar.notify_one();
        dummclass_thread.join();
    }
private:
    void dummclassThreadProc()
    {
          {
            std::unique_lock<std::mutex> mlock(m_mutex);
            std::cout<<"thread waiting\n";
            m_condVar.wait(mlock);
            std::cout<<"thread done waiting\n";
          }

        sleep(1);

        std::unique_lock<std::mutex> mlock(m_mutex);
        while (!stop)//!stop_dummclass.load())
        {

            std::cout<<"got mutex\n";
            m_condVar.wait(mlock);
            std::cout<<"wait done\n";

            {
                std::cout<<"got one\n";
                alarms++;
            }
        }
        std::cout<<"end loop\n";
    }
};

int main()
{
  dummclass *x = new dummclass;
  sleep(3);

  {
        std::lock_guard<std::mutex> lock_guard(m_mutex);
  }
  m_condVar.notify_one();

  std::cout<<"done waiting\n";
  sleep(3);

  for(int i=0;i<13;i++)
  {
       {
        std::cout<<"signal "<<i<<"\n";
        std::lock_guard<std::mutex> lock_guard(m_mutex);
       }
        m_condVar.notify_one();
  }
  delete x;
}

奇怪的是,循环外的初始等待和信号实际上工作正常。我不明白我做了什么错误,以至于类线程中的while循环没有捕获来自主线程的任何信号,但是当我删除它时它从虚拟类的析构函数中捕获一个信号。这是输出:

  

线程等待
  完成等待   线程完成等待
  得到了互斥   信号0信号1信号2信号3信号4信号5信号6信号7信号   8信号9信号10信号11信号12
  警报:0
  等完了   有一个结束循环

编辑:似乎在main()for循环中添加1秒睡眠可以解决问题。 for循环是否有可能在wait()设法唤醒并锁定互斥锁之前获取互斥锁?

for(int i=0;i<13;i++)
  {
       {std::cout<<"signal "<<i<<"\n";
        std::lock_guard<std::mutex> lock_guard(m_mutex);}
        m_condVar.notify_one();
        sleep(1);
  }

有人可以告诉我出了什么问题吗?

感谢。

2 个答案:

答案 0 :(得分:1)

执行等待的对象在处理信号之前获得delete d。由于delete发生在已知正在运行的线程上,因此它有一个很好的机会首先被执行。特别是它也可能再次重新获取锁:由于notify_one()在互斥锁被锁定时完成,wait()线程无法获取它并将重新进入休眠状态,等待互斥锁成为释放。这为信令线程提供了重新获取锁定的机会。导致信令线程等待的唯一强制同步是join(),它确实为等待线程提供了执行的机会。

请注意,条件变量的信号 not 传递给等待线程的信号。它们基本上是唤醒电话。一旦发出信号,等待线程最终将被唤醒。但是,许多信号可以在它实际发送之前传递。

答案 1 :(得分:1)

  

我不明白我做了什么错误使得while循环里面   class thread没有捕获来自主线程的任何信号

即使发送了多个通知,线程也只会收到一个通知。

notify_one()来电

  • 并不意味着当前线程将停止并等待另一个线程。
  • 这只是意味着另一个线程必须在某个时刻醒来,因为可能会发生一些它会感兴趣的事情。

另请注意,std::condition_variable::wait可能会遇到spurious wakeup,因此它甚至可能无法做任何事情或收到“真实”信号。

解决方案是提供谓词作为wait()调用的参数。然后谓词可以检查是否有信号(通过为此目的提供的变量并且仅在锁定下更改)并且还可以检查程序是否已经停止。

在下面的更新程序中,我在等待中添加了一个谓词并进行了一些小的更改。该程序仅通知under lock, but you might choose not to

// Example program - modified
#include <iostream>
#include <string>
#include <atomic>
#include <thread>
//#include <unistd.h>
#include <mutex>
#include <condition_variable>
#include <chrono>

std::mutex m_mutex;
std::condition_variable m_condVar;
bool signal_waiting{false};
bool stop{false};

class dummclass
{
    int alarms{};
    std::thread dummclass_thread{[this](){dummclassThreadProc(); }};

public:
    ~dummclass()
    {
        std::cout << "Alarms: " << alarms << "\n";
        //signal thread before joining
        {
            std::lock_guard<std::mutex> lock_guard(m_mutex);
            stop = 1;
            m_condVar.notify_one();
        }
        dummclass_thread.join();
    }
private:
    void dummclassThreadProc()
    {
        {
            std::unique_lock<std::mutex> mlock(m_mutex);
            std::cout << "thread waiting\n";
            m_condVar.wait(mlock);
            std::cout << "thread done waiting\n";
        }

        std::this_thread::sleep_for(std::chrono::seconds{1});

        while(!stop)//!stop_dummclass.load())
        {
            std::unique_lock<std::mutex> mlock(m_mutex);
            std::cout << "got mutex\n";
            //m_condVar.wait(mlock);
            m_condVar.wait(mlock, [](){return signal_waiting || stop; });
            if(stop)
                break;
            std::cout << "wait done\n";

            std::cout << "got one\n";
            alarms++;
            signal_waiting = false;
            m_condVar.notify_one();
        }
        std::cout << "end loop\n";
    }
};

int main()
{
    dummclass *x = new dummclass;
    //sleep(3);
    std::this_thread::sleep_for(std::chrono::seconds{1});

    {
        std::lock_guard<std::mutex> lock_guard(m_mutex);
        m_condVar.notify_one();
    }

    std::cout << "done waiting\n";
    //sleep(3);
    std::this_thread::sleep_for(std::chrono::seconds{1});

    for(int i = 0; i<13; i++)
    {
        {
            std::cout << "signal " << i << "\n";
            std::unique_lock<std::mutex> lock(m_mutex);
            m_condVar.wait(lock, [](){return !signal_waiting ; });
            signal_waiting = true;
            m_condVar.notify_one();
        }
    }
    delete x;
}