多线程C++

std::thread

构造函数和赋值操作

1、默认构造函数,创建一个空的std::thread执行对象;

2、初始化构造函数,创建一个 std::thread 对象,该std::thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出;

3、拷贝构造函数(被禁用),std::thread对象无法进行拷贝构造;

4、移动构造函数,std::move(),调用成功后,原对象不代表任何std::thread的执行对象

成员函数

std::thread提供一些成员函数类辅助线程

get_id:获取线程ID,返回类型为std::thread::id对象

joinable:检查线程是否可被join检查当前对象线程是否表示一个活动的执行线程,有默认线程创建的线程是不能被join的

joinjoin线程会阻塞当前线程,直到由*this所表示的线程执行完毕join才返回

detachdetach线程将当前线程对象多代表执行实例与该线程对象分离,不会阻塞当前线程,调用detach()函数后:

  • thread对象本身不再代表任何的线程执行实例,但是线程仍然在运行。
  • joinable() == false,thread对象不再需要join。
  • get_id() == std::thread::id(),也就是0x0

std::this_thread 命名空间中相关辅助函数

get_id: 获取当前的线程 ID。 

sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。

sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比 sleep_duration 所表示的时间片更长。

互斥量std::mutex、锁std::lock_guard

C++11中std::mutex类型

  1. std::mutex:最基本的mutex类
  2. std::recursive_mutex:递归mutex类,可多次锁定而不死锁
  3. std::time_mutex:定时mutex类,可以锁定一定时间
  4. std::recursive_time_mutex:定时递归mutex类
  5. std::lock_guard:方便线程对互斥量上锁,自动解锁
  6. std::unique_lock:方便线程对互斥量上锁,提供更好是上锁和解锁控制
  7. std::try_lock:尝试同时对多个互斥量上锁。
  8. std::lock:可以同时对多个互斥量上锁。
  9. std::call_once:如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数只调用一次。

std::mutex

std::mutex是C++中最基本的互斥量,提供了独占所有权的特性,std::mutex提供了以下成员函数

  1. 构造函数:std::mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象是处于unlocked状态的。
  2. lock():调用线程将锁住该互斥量,线程调用该函数会发生以下3种情况:

(1)如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁。

(2)如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。

(3)如果当前互斥量被当前调用线程锁住,则会产生死锁,,也就是说同一个线程中不允许锁两次。

  1. unlock():解锁,释放对互斥量的所有权。
  2. try_lock():尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞,线程调用该函数会出现下面3种情况:

(1)如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用unlock释放互斥量。

(2)如果当前互斥量被其他线程锁住,则当前调用线程返回false,而并不会被阻塞掉。

(3)如果当前互斥量被当前调用线程锁住,则会产生死锁。

std::recursive_mutex

可以避免死锁,使用时,lock和unlock的数量必须相等,否则会出错,std::recursive_mutex的性能会比较差一些

std::time_mutex和std::recursive_timed_mutex

这两种互斥量类型和不带time的相比,多了两个成员函数:

  1. try_lock_for():函数参数表示一个时间范围,在这一段时间范围之内线程如果没有获得锁则保持阻塞;如果在此期间其他线程释放了锁,则该线程可获得该互斥锁;如果超时(指定时间范围内没有获得锁),则函数调用返回false。
  2. try_lock_until():函数参数表示一个时刻,在这一时刻之前线程如果没有获得锁则保持阻塞;如果在此时刻前其他线程释放了锁,则该线程可获得该互斥锁;如果超过指定时刻没有获得锁,则函数调用返回false。

std::lock_guard和std::unique_lock

std::lock_guard是一个模板类,模板类型可以是以上的四种锁,用于自动锁定解锁,直到对象作用域结束

std::unique_lock也支持std::lock_guard的功能,但是区别在于它提供跟多的成员函数使用更加灵活,(可以多次上锁和解锁)并且能够和condition_variable一起使用来控制线程同步

std::try_lock、std::lock、std::call_once

std::try_lock支持尝试对多个互斥量进行锁定,尝试锁定成功返回-1,否则返回锁定失败的互斥量的位置,例如第一个锁定失败返回0、第二个失败返回1。

std::lock支持对多个锁锁定,并且避免死锁的出现。

std::call_once的作用是即使在多线程的情况下,也只执行一次指定的可调用对象(可以是函数、成员函数、函数对象、lambda函数),需要通过配合std::once_flag实现。

若在调用 call_once 的时刻, flag 指示已经调用了f指定的可调用对象,则 call_once 立即返回,就是说不再执行可调用对象。

否则,call_once 会调用指定的可调用对象。若该调用对象抛异常,则传播异常给 call_once 的调用方,并且不翻转 flag ,让下一次调用仍然执行。若该调用正常返回,则翻转 flag ,并保证同一 flag不在执行可调用对象。

异步线程操作

std::async和std::future

future<returnType> f = async(launch __policy, _Fp&& __f, _Args&&... __args);

std::async

创建一个异任务(不一定创建新线程)返回一个future对象

async第一个参数,为可选项:

  • launch::async 强制开启一个新线程,若无法开启新线程则会报错;
  • launch::deferred 延迟调用,等到对象实例调用get()、wait()函数时才开始调用,并没有创建新线程;
  • launch::any 即 launch::async|launch::deferred 该函数自动选择策略(在某一时刻)。这取决于系统和库的实现,它们通常针对系统中并发性的当前可用性进行优化。

 async第二个参数为调用的子函数地址,即线程入口函数

async第三个参数为子函数(线程入口函数)的参数

std::future

https://www.cnblogs.com/haippy/p/3239248.html

https://www.cnblogs.com/haippy/p/3279565.html

https://blog.csdn.net/watson2016/article/details/52860797

promise  把future绑定到promise上,通过get_future()获取线程函数返回值,可用于线程间数据传递

std::promise<int> prom; // 生成一个 std::promise<int> 对象.
std::future<int> fut = prom.get_future(); // 和 future 关联.
std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t.
prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步.
t.join();

packaged_task  包装一个可调用对象,并允通过get_future()许异步获取该调用对象产生的结果,为一个future对象。

future 可以用来获取异步任务的结果,一般搭配promise、packaged_task或async使用

std::future::get 一共有三种形式,如下表所示

返回值描述
future_status::ready共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。
future_status::timeout超时,即在规定的时间内共享状态的标志没有变为 ready。
future_status::deferred共享状态包含一个 deferred 函数。

std::future 对象相关联的共享状态标志变为 ready 后,调用该函数将返回保存在共享状态中的值,如果共享状态的标志不为 ready,则调用该函数会阻塞当前的调用者,而此后一旦共享状态的标志变为 ready,get 返回 Provider 所设置的共享状态的值或者异常(如果抛出了异常)。

shared_futrue

#include <iostream>
#include <memory>
#include <future>

using namespace std;

int myThread(int mydate)
{
	cout << "mythread() start ,thraeadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	return mydate;
}

void myThread2(std::future<int> &tmp)
{
	cout << "mythread() start,thraeadid = " << std::this_thread::get_id() << endl;
	auto result = tmp.get();
	cout << "mythread2 result = " << result << endl;
	return;
}

int main()
{
	cout << "mian threadid = " << std::this_thread::get_id() << endl;
	//std::future<int> result = std::async(myThread, 520);
	//cout << result.get() << endl;

	std::packaged_task<int(int)>mypt(myThread);//用packaged_task把 线程入口函数包装起来
	std::future<int> result = mypt.get_future();
	std::thread t2(myThread2, std::ref(result));
	std::thread t1(std::ref(mypt), 520);			//创建线程并开始执行
	t1.join();					
	t2.join();

	cout << "I love xiaoyan!" << endl;
	
	unique_ptr<int> uptr;
	system("PUASE");
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值