我正在尝试使用类成员运行一个带有函数的线程,并使用条件变量等待主线程发出信号并添加线程发出信号的时间。这是代码:
// 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);
}
有人可以告诉我出了什么问题吗?
感谢。
答案 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;
}