我是靠谱客的博主 耍酷百合,最近开发中收集的这篇文章主要介绍C++thread的使用(C++多线程相关),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • C++thread的两种“死法”
    • C++thread不完美的两种线程等待处理任务的方法
    • C++thread伪多线程例子
    • C++thread通过代码独立性考虑多线程的优劣
    • C++thread condition_variable和unique_lock的搭配使用来唤醒线程

C++thread的两种“死法”

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 <iostream>
#include <thread>
#include <chrono>

void joinWorker() {

}

class Obj {
	public:
		Obj() { std::cout << "hello ";}
		~Obj() { std::cout << "worldn";}
};

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

C++thread不完美的两种线程等待处理任务的方法

有的时候开线程并不需要线程马上去做一些事情需要满足一定条件才做,比如接收网络数据包需要线程进入等待
用while死循环判断线程是否该做某些事CPU占用率会变得非常高
在这里插入图片描述
一种解决方法是在while循环里让线程释放CPU资源但不一定生效,因为可能当一个线程释放资源后马上被其他线程抢占了

另一种方法添加一个时间片,过一段时间执行while循环
在这里插入图片描述

printf是默认线程安全的,在单个函数下运行是不会出错的,cout没这个保证

C++thread伪多线程例子

以下是单线程多线程分别处理数据,其中多线程有可能处理不了所有数据则程序会进入死循环,例子中假定work能处理完所有数据
在这里插入图片描述
在这里插入图片描述
上述多线程例子主线程和子线程锁用得非常密集,一个地方释放锁下个线程马上又进行上锁
好比一个包子只能给一条狗吃,另外四条只能光看着。。。实际上只能换伪多线程,依然是单线程在运行

针对以上问题可以在塞数据的时候减少加锁的代码,减少锁空间
在这里插入图片描述
但结果可能更糟糕,主线程进入sleep时会导致子线程while(!quit)在频繁的加锁和放锁,CPU占用率也会大大提高
在这里插入图片描述

C++thread通过代码独立性考虑多线程的优劣

从代码一部分需要公共用到的东西和一部分可以独立完成的东西可以看出多线程的好处与坏处
以下例子四个线程都会用到globallist
在这里插入图片描述
可以用sleep进行CPU占用率上的优化
在这里插入图片描述
将需要处理的数据单独抽出来变小lock区域也不能解决问题
在这里插入图片描述
在这里插入图片描述
多了一次拷贝,多了一次构造和move还用了原子操作,速度并不能提高
在这里插入图片描述

所以多线程交互最好的方式应该是有数据让多个线程去处理,没有数据就让线程休息,不要用lock不断地加锁解锁

C++thread condition_variable和unique_lock的搭配使用来唤醒线程

用std::condition_variable cv定义信号唤醒线程
用cv.notify_one()来唤醒一个线程
用cv.notify_all()来唤醒所有线程
用std::unique_lockstd::mutex 和cv.wait配合,wait把lock释放然后等待后面的条件,当后面条件达成时锁重新得到继续执行以下代码,unique_lock ()允许中间释放锁

//std::lock_guard<std::mutex> 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 <thread>
#include <mutex>
#include <atomic>
#include <vector>
#include <iostream>
#include <chrono>
#include <list>
#include <string>
#include <ctime>
#include <cstring>
#include <condition_variable>
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<std::mutex> 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;
}

最后

以上就是耍酷百合为你收集整理的C++thread的使用(C++多线程相关)的全部内容,希望文章能够帮你解决C++thread的使用(C++多线程相关)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部