thread默认析构函数是调用了abort()让程序中止
//abort(errorcode);
子线程可以自己管理资源也可以在子线程结束时自动将线程杀死,需要程序员显示指定用的是哪种行为,一般都自己来管理资源由j.join()来结束线程,在join前要判断是否joinable()
if (j.joinable())
j.join();
if(w.joinable()){
w.join();
}
另一种让子线程代码执行完自动杀死线程,调用detach()即可
生命周期短的用detach,比整个生命周期是短的,不然可能主函数自己的析构函数没调用
void detachWorker() {
Obj obj;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main() {
std::thread j(joinWorker);
ThreadGuard guard(j);
std::thread w(detachWorker);
w.detach();
//abort(errorcode);
return 0;
}
有的时候如果在join前要抛出异常时可以自己定义线程保护类
class ThreadGuard {
public:
ThreadGuard(std::thread& t) : m_thread(t) {}
~ThreadGuard() { if(m_thread.joinable()) m_thread.join();}
private:
std::thread& m_thread;
};
#include
#include
#include
void joinWorker() {
}
class Obj {
public:
Obj() { std::cout << "hello ";}
~Obj() { std::cout << "world\n";}
};
void detachWorker() {
Obj obj;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
class ThreadGuard {
public:
ThreadGuard(std::thread& t) : m_thread(t) {}
~ThreadGuard() { if(m_thread.joinable()) m_thread.join();}
private:
std::thread& m_thread;
};
int main() {
std::thread j(joinWorker);
ThreadGuard guard(j);
std::thread w(detachWorker);
w.detach();
//abort(errorcode);
return 0;
}
有的时候开线程并不需要线程马上去做一些事情需要满足一定条件才做,比如接收网络数据包需要线程进入等待
用while死循环判断线程是否该做某些事CPU占用率会变得非常高
一种解决方法是在while循环里让线程释放CPU资源但不一定生效,因为可能当一个线程释放资源后马上被其他线程抢占了
另一种方法添加一个时间片,过一段时间执行while循环
printf是默认线程安全的,在单个函数下运行是不会出错的,cout没这个保证
以下是单线程多线程分别处理数据,其中多线程有可能处理不了所有数据则程序会进入死循环,例子中假定work能处理完所有数据
上述多线程例子主线程和子线程锁用得非常密集,一个地方释放锁下个线程马上又进行上锁
好比一个包子只能给一条狗吃,另外四条只能光看着。。。实际上只能换伪多线程,依然是单线程在运行
针对以上问题可以在塞数据的时候减少加锁的代码,减少锁空间
但结果可能更糟糕,主线程进入sleep时会导致子线程while(!quit)在频繁的加锁和放锁,CPU占用率也会大大提高
从代码一部分需要公共用到的东西和一部分可以独立完成的东西可以看出多线程的好处与坏处
以下例子四个线程都会用到globallist
可以用sleep进行CPU占用率上的优化
将需要处理的数据单独抽出来变小lock区域也不能解决问题
多了一次拷贝,多了一次构造和move还用了原子操作,速度并不能提高
所以多线程交互最好的方式应该是有数据让多个线程去处理,没有数据就让线程休息,不要用lock不断地加锁解锁
用std::condition_variable cv定义信号唤醒线程
用cv.notify_one()来唤醒一个线程
用cv.notify_all()来唤醒所有线程
用std::unique_lockstd::mutex 和cv.wait配合,wait把lock释放然后等待后面的条件,当后面条件达成时锁重新得到继续执行以下代码,unique_lock ()允许中间释放锁
//std::lock_guard lock(mutex);
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [] { return quit || !globalList.empty(); });
if (quit)
return;
直接用cv.wait(lock)可能会无缘无故被唤醒
发送信号可以没有在mutex保护下发
以下关于unique_lock和wait完整代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class Message {
public:
const std::string& data() const { return m_data;}
Message(std::string d = std::string()) : m_data(std::move(d)) {}
private:
std::string m_data;
};
std::atomic<int> totalSize{0};
std::mutex mutex;
std::condition_variable cv;
std::atomic<bool> ready{false};
bool quit{false};
std::list<Message> globalList;
void worker(int i) {
while(!ready) {
}
Message msg;
while (!quit) {
{
//std::lock_guard lock(mutex);
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [] { return quit || !globalList.empty(); });
if (quit)
return;
auto iter = globalList.begin();
msg = std::move(*iter);
globalList.erase(iter);
}
totalSize += strlen(msg.data().c_str());
}
}
int main() {
const auto threadCount = 4;
for (int i = 0; i < 500000; ++i)
globalList.push_back("this is a test" + std::to_string(i));
std::vector<std::thread> pool;
for(int i = 0; i< threadCount; ++i) {
pool.emplace_back(worker, i);
}
ready = true;
for(int i = 0; i < 300000; ++i) {
//std::this_thread::sleep_for(std::chrono::milliseconds(1));
{
std::lock_guard<std::mutex> lock(mutex);
globalList.push_back(std::string("second"));
}
cv.notify_one();
}
while(true) {
std::lock_guard<std::mutex> lock(mutex);
if(globalList.empty()) {
quit = true;
cv.notify_all();
break;
}
}
for (auto &v : pool) {
if (v.joinable())
v.join();
}
std::cout << "total size is " << totalSize << std::endl;
}