您当前的位置: 首页 >  c++

龚建波

暂无认证

  • 4浏览

    0关注

    313博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

复习C++标准库多线程的基本使用

龚建波 发布时间:2021-05-08 00:21:11 ,浏览量:4

参考书籍:《C++并发编程实战》第一版&&第二版

参考文档:https://zh.cppreference.com/w/cpp/thread

第一章:你好,C++并发世界

(略)

第二章:管理线程

(2021-05-07 笔记)

类 std::thread 表示单个执行线程,thread 对象允许 move 但不能 copy 。 构造函数:

template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
构造 std::thread 实例并将它关联至新创建的执行线程。复制或者移动 func 和 args 的每个元素到内部存储中,并持续在新的执行线程的整个声明周期。在新的线程上执行 invoke。
移动或按值复制线程函数的参数。若需要传递引用参数给线程函数,则必须包装它(例如用 std::ref 或 std::cref )。
忽略来自函数的任何返回值。若函数抛异常,则调用 std::terminate 。为将返回值或异常传递回调用方线程,可使用 std::promise 或 std::async 。

基本操作:

bool joinable() const noexcept; 
检查 std::thread 对象是否标识为活跃的执行线程。具体而言,若 get_id()!=std::thread::id() 则返回 true 。故默认构造的 thread 不可 join。
结束执行代码,但仍未 join 的线程仍被当作活跃的执行线程,从而可 join。
调用 join() 或者 detach() 后也为 false 。

std::thread::id get_id() const noexcept;
返回标识与 *this 关联的线程的 std::thread::id 。若无关联的线程,则返回默认构造的 std::thread::id 。

static unsigned int hardware_concurrency() noexcept;
返回实现支持的并发线程数。应该只把该值当做提示。

void join();
阻塞当前线程直至 *this 所标识的线程结束其执行。

void detach();
从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。
调用 detach 后 *this 不再占有任何线程。

void swap( std::thread& other ) noexcept;
交换两个 thread 对象的底层 handle 。

练习:

#include 
#include 

void func1(int a, int& b){
	b = a;
}

//守护类,释放时自动join
class scoped_thread
{
public:
	explicit scoped_thread(std::thread& t)
		: th(std::move(t)){
	}
	~scoped_thread(){
		if (th.joinable())
			th.join();
	}
	scoped_thread(const scoped_thread&) = delete;
	scoped_thread& operator = (const scoped_thread&) = delete;

private:
	std::thread th;
};

int main()
{
	std::thread t1([] { });
	if (t1.joinable()) {
		//等待线程结束
		t1.join();
	}

	std::thread t2([] { });
	if (t2.joinable()) {
		//分离不等待,不会在释放时调用std::terminate
		t2.detach();
	}

	//线程函数的参数按值移动或复制。
	//如果需要将引用参数传递给线程函数,则必须将其包装(例如,使用std::ref或std::cref)。
	int a = 1, b = 2;
	std::thread t3(func1, a, std::ref(b));
	if (t3.joinable())
		t3.join();

	//std::thread可移动但不可复制
	//std::thread t4(std::move(t3));
	scoped_thread scope(t3);

	return 0;
}
第三章:在线程间共享数据

(2021-05-16 笔记)

类 std::mutex 是能用于保护共享数据免受从多个线程同时访问的同步原语,即互斥锁、互斥元、互斥体。

std::mutex 基本操作:

void lock();
锁定互斥。若另一线程已锁定互斥,则到 lock 的调用将阻塞执行,直至获得锁。

bool try_lock();
尝试锁定互斥。立即返回。成功则锁定并返回 true ,否则返回 false 。

void unlock();
解锁互斥。互斥必须为当前执行线程所锁定,否则行为未定义。

std::timed_mutex 多两个接口:

template< class Rep, class Period >
bool try_lock_for( const std::chrono::duration& timeout_duration );
尝试锁互斥。阻塞直到经过指定的 timeout_duration 或得到锁,取决于何者先到来。成功获得锁时返回 true , 否则返回 false 。

template< class Clock, class Duration >
bool try_lock_until( const std::chrono::time_point& timeout_time );
尝试锁互斥。阻塞直至抵达指定的 timeout_time 或得到锁,取决于何者先到来。成功获得锁时返回 true ,否则返回 false 。

死锁:两个或多个线程互相持有对方所需要的资源,导致互相等待。

初始化:C++11 开始可用 static 或者 std::call_once 来防范初始化时的多线程竞争。

练习:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

//1.自定义多线程操作安全的链表
template
class MyList
{
public:
	bool pop(T& value) {
		//给需要多线程操作的接口上锁,解决竞争
		std::lock_guard guard(mtx);
		if (data.size() > 0) {
			value = data.front();
			data.pop_front();
			return true;
		}
		return false;
	}

	void push(const T& value) {
		std::lock_guard guard(mtx);
		data.push_back(value);
	}

	//同时操作多个mutex时要避免死锁
	friend void swap(MyList& left, MyList& right) {
		if (&left == &right)
			return;
		// 用 std::lock 获得二个锁,而不用担心死锁
		std::lock(left.mtx, right.mtx);
		std::lock_guard lguard(left.mtx, std::adopt_lock);
		std::lock_guard rguard(right.mtx, std::adopt_lock);
		// 等价代码(若需要 unique_locks ,例如对于条件变量)
        //std::unique_lock lk1(left.mtx, std::defer_lock);
        //std::unique_lock lk2(right.mtx, std::defer_lock);
        //std::lock(lk1, lk2);
        // C++17 中可用的较优解法
        //std::scoped_lock lk(left.mtx, right.mtx);

		std::swap(left.data, right.data);
	}

private:
	std::list data;
	//加mutable是为了在const接口中使用
	mutable std::mutex mtx;
};

//2.初始化时保护共享数据
std::once_flag onceflag;
void init() {

}
int* instance() {
	static int i;
	return &i;
}

//3.递归锁(C++11) 
class DemoA {
public:
	void func1() {
		std::lock_guard lk(m);
		//递归锁定了func2中的
		func2();
	}
	void func2() {
		std::lock_guard lk(m);
	}
private:
	std::recursive_mutex m;
};

//4.读写锁(C++17)
class ThreadSafeCounter {
public:
	// 多个线程/读者能同时读计数器的值。
	unsigned int get() const {
		//share共享,只读
		std::shared_lock lock(mtx);
		return value;
	}
	// 只有一个线程/写者能增加/写线程的值。
	void increment() {
		//unique独占,读写
		std::unique_lock lock(mtx);
		value++;
	}
	// 只有一个线程/写者能重置/写线程的值。
	void reset() {
		std::unique_lock lock(mtx);
		value = 0;
	}

private:
	mutable std::shared_mutex mtx;
	unsigned int value = 0;
};

//5.时间限定的锁定(C++11)
std::timed_mutex tmtx;
void time_lock() {
	//指定时间段,如10ms后超时,或者unlock
	//std::chrono::duration
	//if (tmtx.try_lock_for(std::chrono::minutes(1))) {
	if (tmtx.try_lock_for(std::chrono::duration (1))) {
		//...
		tmtx.unlock();
	}
	//指定时间点,如1点钟超时,或者unlock
	//std::chrono::time_point
	auto t1 = std::chrono::high_resolution_clock::now();
	if (tmtx.try_lock_until(t1 + std::chrono::minutes(1))) {
		//...
		tmtx.unlock();
	}
}

int main()
{
	//1.线程安全链表
	MyList queue;
	queue.push(10);
	MyList queue2;
	queue2.push(20);
	swap(queue, queue2);

	int value;
	if (queue.pop(value)) {
		std::cout             
关注
打赏
1655829268
查看更多评论
0.0882s