因为对条件变量不了解,只是看过。这次在插件管理器中要实现插件的加载和插件的启停的分离(原来时将插件加载和启动放到了一起,插件加载后就直接启动,然后从插件对应的传感器中读取数据、存储)。现在要求在插件管理器在自身进程中将插件加载进来,每个插件作为一个单独的线程,线程启动之后,处于休眠状态,这样不消耗cpu资源。使用方的程序是一个单独的进程,它决定什么时候启动什么插件。
这个需求给我们带来2个问题:(1)进程间怎么通信,因为原来插件的管理都在插件管理器这个进程中,插件对象和插件管理器对象都是已知的,或者说调用加载插件和启动插件相关函数的对象是同一个,现在要在插件管理器对象中加载插件,而在另外一个进程中启动插件,那么这两个进程怎么共用一个插件对象;(2)插件的启动停止需要信号量或者条件变量来控制,原来并没有接触过多。
针对上述问题,对于进程间通信,我们准备采用黑板的机制,来监听用户需要我们启停哪个插件,通过黑板传递要启停的插件的描述,比如插件名称(目前和我和烟台时通过storePath这个字符串来唯一建立联系的,但是storePath和插件名又在同一个sensor类对象中),所以这一步是可以打通的,烟台接收用户传来的要启停的插件名,写到黑板上;我这边的callback接收插件名,然后从map中找到对应的插件对象的指针,然后调用插件的notify_wake或者notify_wait()(这俩函数都要在SensorPluginBase和LabelGeneratorBase中增加),实现对条件变量的激活和休眠,从而实现线程的启动和暂停。
#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <sys/time.h>
#include <unistd.h>
//这个类似具体的插件类
class testcond
{
private:
std::mutex mtx;//互斥锁
std::condition_variable cv;//条件变量
bool ready = false; // 防止条件变量被其它notify误激活,属于功能加强
public:
testcond(/* args */) {}
void gendata();//线程函数
void notifyWake();//激活线程
void setWait();//挂起线程
~testcond() {}
};
void testcond::gendata(/* args */)
{
std::unique_lock<std::mutex> lck(mtx);
std::cout << "1111" << std::endl;
// 如果标志位不为true,则等待
int count = 0;
std::cout<<"ready is "<<ready<<std::endl;
while (1)//持续获取数据
{
while (!ready)//实际上notifyWake就是给ready置位,以跳过本while
{
std::cout << "222" << std::endl;
// 线程被阻塞,直到标志位变为true,阻塞情况下后续代码都不执行
cv.wait(lck);
}
usleep(100*1000);
std::cout << "count is " << count++ << std::endl;
}
//在当前情况下,下面这句永远不会输出
std::cout << "I'm waked by notify thread: "
<< "\n";
}
//激活线程
void testcond::notifyWake()
{
std::unique_lock<std::mutex> lck(mtx);
// 改变全局标志位
ready = true;
// 唤醒本线程
cv.notify_one();
std::cout<<"moti1111111111"<< std::endl;
}
//线程休眠
void testcond::setWait()
{
ready = false;
}
int main()
{
testcond test;
std::thread threads(&testcond::gendata, &test);
sleep(4);
std::cout << "wait done.\n";
test.notifyWake();
// for (auto &t : threads)
{
threads.detach();
}
sleep(2);
test.setWait();
std::cout << "sencond wait" << std::endl;
sleep(2);
test.notifyWake();
std::cout << "sencond wake" << std::endl;
std::cout << "process done.\n";
getchar();
sleep(50);
return 0;
}
wsw@wsw:~/test/c++/conditioncar/src$ ./cond
1111
ready is 0
222
wait done.
moti1111111111
count is 0
count is 1
count is 2
count is 3
count is 4
count is 5
count is 6
count is 7
count is 8
count is 9
count is 10
count is 11
count is 12
count is 13
count is 14
count is 15
count is 16
count is 17
count is 18
sencond wait
count is 19
222
moti1111111111
sencond wake
process done.
count is 20
count is 21
count is 22
count is 23
count is 24
count is 25
count is 26
count is 27
count is 28
count is 29
count is 30
count is 31
count is 32
count is 33
count is 34
count is 35
count is 36
如果没有while(!ready)则每次在while(1)中cv.wait(lck)都会执行,也就是每次都会等待,notify之后,才会继续往后走。
#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <sys/time.h>
#include <unistd.h>
//这个类似具体的插件类
class testcond
{
private:
std::mutex mtx;//互斥锁
std::condition_variable cv;//条件变量
bool ready = false; // 防止条件变量被其它notify误激活,属于功能加强
public:
testcond(/* args */) {}
void gendata();//线程函数
void notifyWake();//激活线程
void setWait();//挂起线程
~testcond() {}
};
void testcond::gendata(/* args */)
{
std::unique_lock<std::mutex> lck(mtx);
std::cout << "1111" << std::endl;
// 如果标志位不为true,则等待
int count = 0;
std::cout<<"ready is "<<ready<<std::endl;
while (1)//持续获取数据
{
// while (!ready)//实际上notifyWake就是给ready置位,以跳过本while
{
std::cout << "222" << std::endl;
// 线程被阻塞,直到标志位变为true,阻塞情况下后续代码都不执行
cv.wait(lck);
}
usleep(100*1000);
std::cout << "count is " << count++ << std::endl;
}
//在当前情况下,下面这句永远不会输出
std::cout << "I'm waked by notify thread: "
<< "\n";
}
//激活线程
void testcond::notifyWake()
{
std::unique_lock<std::mutex> lck(mtx);
// 改变全局标志位
ready = true;
// 唤醒本线程
cv.notify_one();
std::cout<<"moti1111111111"<< std::endl;
}
//线程休眠
void testcond::setWait()
{
ready = false;
}
int main()
{
testcond test;
std::thread threads(&testcond::gendata, &test);
sleep(4);
std::cout << "wait done.\n";
test.notifyWake();
// for (auto &t : threads)
{
threads.detach();
}
sleep(2);
test.setWait();
std::cout << "sencond wait" << std::endl;
sleep(2);
test.notifyWake();
std::cout << "sencond wake" << std::endl;
std::cout << "process done.\n";
getchar();
sleep(50);
return 0;
}
wsw@wsw:~/test/c++/conditioncar/src$ ./cond
1111
ready is 0
222
wait done.
moti1111111111
count is 0
222
sencond wait
moti1111111111
sencond wake
process done.
count is 1
222
那我们猜测notify_one()是不是没有用啊,下面我们注释掉该函数的调用。
#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <sys/time.h>
#include <unistd.h>
//这个类似具体的插件类
class testcond
{
private:
std::mutex mtx;//互斥锁
std::condition_variable cv;//条件变量
bool ready = false; // 防止条件变量被其它notify误激活,属于功能加强
public:
testcond(/* args */) {}
void gendata();//线程函数
void notifyWake();//激活线程
void setWait();//挂起线程
~testcond() {}
};
void testcond::gendata(/* args */)
{
std::unique_lock<std::mutex> lck(mtx);
std::cout << "1111" << std::endl;
// 如果标志位不为true,则等待
int count = 0;
std::cout<<"ready is "<<ready<<std::endl;
while (1)//持续获取数据
{
while (!ready)//实际上notifyWake就是给ready置位,以跳过本while
{
std::cout << "222" << std::endl;
// 线程被阻塞,直到标志位变为true,阻塞情况下后续代码都不执行
cv.wait(lck);
}
usleep(100*1000);
std::cout << "count is " << count++ << std::endl;
}
//在当前情况下,下面这句永远不会输出
std::cout << "I'm waked by notify thread: "
<< "\n";
}
//激活线程
void testcond::notifyWake()
{
std::unique_lock<std::mutex> lck(mtx);
// 改变全局标志位
ready = true;
// 唤醒本线程
// cv.notify_one();
std::cout<<"moti1111111111"<< std::endl;
}
//线程休眠
void testcond::setWait()
{
ready = false;
}
int main()
{
testcond test;
std::thread threads(&testcond::gendata, &test);
sleep(4);
std::cout << "wait done.\n";
test.notifyWake();
// for (auto &t : threads)
{
threads.detach();
}
sleep(2);
test.setWait();
std::cout << "sencond wait" << std::endl;
sleep(2);
test.notifyWake();
std::cout << "sencond wake" << std::endl;
std::cout << "process done.\n";
getchar();
sleep(50);
return 0;
}
输出如下:
ok@ok-TS:~$ ./a.out
1111
ready is 0
222
wait done.
moti1111111111
sencond wait
moti1111111111
sencond wake
process done.
count is 0也没有打印出来,说明是真的卡在了cv.wait(lck)处,后面都不会执行。这也说明该线程没有被激活,cv.wait(lck)所在的while也不会执行,因为卡在了cv.wait处,根本不往下面执行。
我们进一步把while(!ready)也注释掉,两者输出一致。
1111
ready is 0
222
wait done.
moti1111111111
sencond wait
moti1111111111
sencond wake
process done.
这么说来,notify_one还是有用的。

本文探讨了如何在插件管理器中实现插件的加载和启停分离,利用条件变量控制线程的启动和暂停,并通过黑板机制实现进程间通信。
2341

被折叠的 条评论
为什么被折叠?



