C++11:多线程与锁

多线程是小型软件开发必然的趋势。C++11将多线程相关操作全部集成到标准库中了,省去了某些坑库的编译,真是大大的方便了软件开发。多线程这个库简单方便实用,下面给出简单的例子

#include 
#include 
#include 
using namespace std;

volatile int val;
mutex mut;

void icrement () {
	for (int i = 0; i < 100000000; i++) {
		mut.lock ();
		val++;
		mut.unlock ();
	}
}

int main (int argc, char* argv []) {
	//创建两个线程
	thread t1 (icrement);
	thread t2 (icrement);
	//等待两个线程执行完
	t1.join ();
	t2.join ();
	cout << val << endl;
	return 0;
}
概念有点多。首先得引入thread与mutex这两个头文件,其中thread与线程有关,mutex与锁有关。然后是两个全局变量,由于两个线程都会访问,所以加上volatile关键字,代表不要对这变量进行优化,然后是mutex,这个就是锁咯。

然后,下面的main函数中,首先创建两个线程,然后等待两个线程执行完,然后输出结果。最后是increment函数,这函数循环一亿次,不停的加锁解锁,控制着val变量的访问。锁还提供一个函数,比如这样调用 mut.try_lock (); ,立即返回bool类型,true代表加锁成功,false代表加锁失败。

这是最基本的情况。对于普通的加锁解锁操作,使用lock、unlock就够用了。但如果分支太多,这个就不好控制了。C++11提供了一种自动锁的机制,比如上面代码可以替换成这样:

void icrement () {
	for (int i = 0; i < 100000000; i++) {
		//调用即加锁,离开大括号作用域自动解锁
		lock_guard lock (mut);
		val++;
	}
}
自动锁就不用刻意的解锁了,对于多分支的情况相当方便。

除了普通的锁之外,还有三种锁:

recursive_mutex rm;//递归锁
recursive_timed_mutex rtm;//递归超时锁
timed_mutex tm;//超时锁
递归锁用在递归的场合,超时锁也就是普通锁加上超时功能,基本功能与锁相同。

锁方便控制代码逻辑,但如果只是控制访问一个变量的话,有一种更好的选择,那就是原子。详见C++11:原子操作

关于代码逻辑的控制,除了thread之外,还有一种,std::async,用于接收线程函数的返回值。关于返回值,通过thread的引用参数也可以传递,std::async只是让代码逻辑更清晰而已。示例代码如下:

#include 
#include 
#include 

int main (int argc, char* argv []) {
	std::future future = std::async (std::launch::async, [] () {
		std::this_thread::sleep_for (std::chrono::seconds (3));
		return 4;
	});
	std::future_status status;
	do {
		status = future.wait_for (std::chrono::seconds (1));
		if (status == std::future_status::ready) {
			std::cout << "std::future_status::ready" << std::endl;
		} else if (status == std::future_status::timeout) {
			std::cout << "std::future_status::timeout" << std::endl;
		} else if (status == std::future_status::deferred) {
			std::cout << "std::future_status::deferred" << std::endl;
		}
	} while (status != std::future_status::ready);
	std::cout << future.get () << std::endl;
	return 0;
}
使用async首先需要引入future这个头文件。然后是std::async这个函数,它返回一个std::future这个模板类型,类型取决于函数的返回值。

第一个参数代表启动方式,std::launch::async代表调用函数时启动,std::launch::deferred代表创建后挂起线程,当执行std::future<>.get()时才执行线程。

第二个参数传入函数,可以是函数地址、lambda表达式或仿函数等。如果有参数的话,可以在调用std::async时加参数就行了。

这个线程执行两句话,sleep_for这行的意思是线程暂停3秒。个人感觉这语法太复杂了,还不如微软Sleep来的简洁。暂停线程之后,直接返回4。

然后 我们定义一个future的状态,future_status,用于获取线程执行结果。这儿可以直接用get(),表示等待线程执行完,那么就不用future了,也跟单线程没区别了,所以还是用wait_for来获取状态。获取状态后进行判断。如果为std::future_status::ready,代表线程已经执行完毕;如果为std::future_status::timeout,表示等待了wait_for传入的时间之后,线程还没执行完毕,还在继续执行;如果为std::future_status::deferred,代表线程还处于挂起状态。我用的VS2013 Communicaty Update4,发现无论如何也无法处于std::future_status::deferred状态,据说是我这个版本的bug,在VS2015中修复。个人建议,挂起线程这个尽量不用,调用get来获取还不如直接单线程来的直接。

然后是future.get,用于获取线程的返回值。如果线程并没执行完那么就一直等着。除此之外还有一个相似的函数,wait,用来等待线程执行结束,效果差不多。

以上代码不出所料,执行结果如下:

C++11:多线程与锁_第1张图片


你可能感兴趣的:(C++)