C++多线程基础

文章基于C++的std库使用C++多线程,全文使用类伪代码编写。

1 C++多线程基本用法

1.1 C++提供的std::thread类

头文件:#include< thread >
使用方法:

void func1();
void func2();
int main(){
std::thread t1(func1);
std::thread t2(func2);
}

如果把创建线程的过程写在函数中,那要在函数结束之后进行detach ,不然线程就会因为函数的生命周期结束而崩溃。例如:

void func3 () {
std::thread t1(func1);
t1.detach();
}

1.2 C++11获取当前线程ID的方法

两种方式:

  1. 使用std::this_thread类的get_id获取当前线程ID,这是一个静态方法。
    在线程内部在执行获取id
thread t([]
 	 {cout << this_thread::get_id() << endl; }
 	 );

除了这些std::this_thread类还有其他用法

  • 使用std::this_thread::yield()放弃当前线程占用时间片使CPU重新调度以便其它线程执行
  • 使用std::this_thread::sleeo_for()阻塞当前线程一段时间
  • 使用std::this_thread::sleeo_until()阻塞当前线程直到某个时间点
  1. 使用std::thread::idget_id获取指定的线程ID,但是get_id返回的是一个std::thread::id的包装类型,该类型不可以被强转换成整型,所以一般采用一下步骤去做转换:
    • 只用std::cout 这样的输出流做输出,或者先转换成std::ostringstream对象,头文件#include < sstream >
    • 再转换成字符串,然后转换成我们需要的整型
std::thread t1(func1);
//获取线程的ID
std::thread::id t1_id = t1.get_id();
// 先将std::thread::id 转化成 std::ostringstream 对象
std::ostringstream oss
oss << t1_id;
// 再将 oss 转成 string
std::string str = oss.str();
// 最后转换成整型
unsigned long long threadid = std::stoll(str);

1.3 C++ 11 等待线程结果的函数

函数:std::threadjoin 方法用来等待线程退出的方法
注意事项:使用这个函数时必须保证该线程处于运行转台,也就是等待的线程时可以join的,如果需要等待的线程已经退出,则此时调用join方法,程序就会因此崩溃。因此C++11 的线程库提供了一个 joinable 的方法来判断。
使用方法:

int main(){
std::thread t1(func1);
// 等待线程结束再执行下面的代码
if (t1.joinable){
   t1.join();
}
}

2 整型变量的原子操作

举例子:在最int a = 0 ;进行变量自增时 a++ 其实执行的时三条汇编指令,两个线程同时操作自增的时候,会导致编译结果不对。在linux平台下必须先定义再赋值,因为linux不支持原子类型的拷贝构造。

int main(){
std::atomic<int> value;
value = 99;
value++;
} 

在这里插入图片描述在这里插入图片描述

3 C++ 11/14/17 线程同步对象

3.1 mutex 的使用

在这里插入图片描述
类型:std::mutex 包含头文件< mutex >
用法:

std::mutex my_mutex
void func1(){
my_mutex.lock();
/* ------省略------*/
my_mutex.unlock();
}

为了避免死锁,std::mutex.lock 和 std::mutex.unlock() 会成对使用。

3.2 封装mutex的接口

  • lock_guard----c++11----基于作用域的互斥量管理
  • unique_lock----c++11----更加灵活的互斥量管理
  • shared_lock----c++14----共享互斥量管理
  • scoped_lock----c++17----多互斥量避免死锁的管理

lock_guard为例:

void func(){
	std::lock_guard<std::mutex> guard(mymutex);
	// 这里放被保护的资源操作
}

mymutex是是std::mutex类型,guard对象的构造函数会自动调用mymutex.lock方法进行加锁,在guard出了作用域的时候析构函数会自动调用mymutex.unlock()进行解锁。

注意

  1. mymutex的生命周期一定要必须长与func函数的作用域,很多人会写成这样对导致无法在多线程调用该函数时保护指定的对象。
void func(){
	std::mutex mymutex;
	std::lock_guard<std::mutex> guard(mymutex);
	// 这里放被保护的资源操作
}
  1. 不要重复对mutex进行加锁,会导致崩溃。

3.3 std::shared_mutex

shared_mutex在C++17才开始有的。底层实现的是读写锁,也就是说,多个线程对共享资源读且少许线程对共享资源写的情况下,shared_mutexmutex效率更高。

std::shared_mutex用法:

  1. 提供lockunlock分别获取写锁或解除写锁;用lock_sharedunlock_shared分别获取读锁和接触读锁。
  2. 另外,提供了std::unique_lockstd::shared_lock配合使用的两个对象,这个两个对象在构造时自动对std::shared_mutex加锁,析构时自动解锁。前者对应写锁,后者对应读锁。

3.4 std::condition_variable

提供了wait系列方法(wait, wait_for, wait_until 方法),发送条件信号时使用notify方法(notify_onenotify_all ).使用std::condition_variable对象时需要绑定std::unique_lock 或者 std::lock_guard 对象。condition_variable不需要显示的初始化和销毁。

#include <iostream>                // std::cout
#include <thread>                // std::thread
#include <mutex>                // std::mutex, std::unique_lock
#include <condition_variable>    // std::condition_variable
 
std::mutex mtx; // 全局互斥锁.
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.
 
void do_print_id(int id)
{
	std::unique_lock <std::mutex> lck(mtx);
 
	while (!ready)
	{
		/*
			如果标志位不为 true, 则等待...
			当前线程被阻塞, 当全局标志位变为 true 之后, 线程被唤醒, 继续往下执行打印线程编号id.
		*/
		cv.wait(lck);
	}
	std::cout << "thread " << id << '\n';
}
 
void go()
{
	std::unique_lock <std::mutex> lck(mtx);
	ready = true; // 设置全局标志位为 true.
	cv.notify_all(); // 唤醒所有线程.
}
 
int main()
{
	std::thread threads[10];
	// spawn 10 threads:
	for (int i = 0; i < 10; ++i)
		threads[i] = std::thread(do_print_id, i);
 
	std::cout << "10 threads ready to race...\n";
	go(); // go!
 
	for (auto & th : threads)
		th.join();
 
	return 0;
}

4 多线程使用锁的经验总结

  1. 减少锁的使用次数
  2. 明确锁的范围
  3. 减少锁的使用粒度,例如lock_guard用大括号包住要操作的部分
  4. 尽量避免使用try_lock 请求锁,以免出现活锁

5 线程局部存储thread_local

看明白了再更新。。。。。。

6 线程池设计

https://www.cnblogs.com/zhaocs/articles/13945197.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值