C++11多线程与并发控制

C++11中封装了一套跨平台的线程库以及配套的并发控制库,这里一起记录一下

一、Thread库

C++11中封装了一套线程库,位于thread文件中,具有基本的线程需要的各类函数。

如下显示了一个基本的线程操作流程:

#include 
#include >
using namespace std;

void ThreadFunc(int nThreadNum)
{
	cout << "INFO: Num is " << nThreadNum << endl;
}

int main()
{
	thread TestThread(ThreadFunc, 1);
	cout << TestThread.get_id() << endl;

	TestThread.join();
	return 0;
}

线程初始化时传入的第一个参数为线程执行的函数,后续可以传入可变参数用于线程函数的参数。主线程可以用get_id获取对应的线程id,需要注意的是,此id并不是系统分配的那个id,获取系统id需要各个操作系统原生的接口。除了一般的函数,也可以传入类对象的成员函数,如

ThreadFuncObj TestObj;
thread TestThread(&ThreadFuncObj::ThreadFunc, &TestObj, 1);

注意上面代码中,由于ThreadFuncObj::ThreadFunc是非静态,因此必须加&来创建一个该类型的函数指针,如果是静态成员函数则不用加,此时编辑器会默认是一个函数指针

二、互斥量mutex

c++11也提供了一套mutex库,用于多线程对共享变量的访问控制。所有相关类都在头文件中。一共有4个互斥类

序号 名称 用途
1 std::mutex 最基本也是最常用的互斥类
2 std::recursive_mutex 同一线程内可递归(重入)的互斥类
3 std::timed_mutex 除具备mutex功能外,还提供了带时限请求锁定的能力
4 std::recursive_timed_mutex 同一线程内可递归(重入)的timed_mutex

基本操作是locktry_lockunlock三种。下面是个简单示例

#include 
#include 
#include 
using namespace std;

void AddCount(mutex &mutexTest, int &nCount)
{
	for (int i = 0; i < 1000; ++i)
	{
		mutexTest.lock();
		++nCount;
		mutexTest.unlock();
	}
}

int main()
{
	thread arrThreads[5];
	mutex mutexTest;
	int nCount = 0;

	for (auto& threadIte : arrThreads)
	{
		threadIte = thread(AddCount, ref(mutexTest), ref(nCount));
	}
	for (thread& threadIte : arrThreads)
	{
		threadIte.join();
	}
	cout << nCount << endl;

	return 0;
}

在上面例子中,如果把lock改为try_lock,则可能导致最终输出小于5000,此外还需注意两点:

  1. C++11新增的for(a : b)这种操作类似于python里面的for a in b,注意,如果要在遍历时操作b里面的a,如改变a的值等,则需要加引用符号&。不加也能操作,但对象就是一个拷贝的对象,而不是b里面的a。如上述例子,threadIte没有加引用,则会报错,因为thread不支持拷贝构造。
  2. 对于线程的传参,需要用ref()来标识引用参数,不然会报错

三、MutexLockGuard

C++的RAII特性被应用在很多地方,mutex库也提供了一套lock_guard类,使得程序可以实现在构造函数中锁定mutex,在析构中解锁。

最基础的是lock_guard模板

lock_guard _guard(mutexTest);

lock_guard模板提供了最基本的RAII特性来操作mutex。此外还可以用unique_lock,unique_lock提供了更多的接口,比如构造之后还可以进行解锁,而不用非得等到析构的时候才解锁

unique_lock _guard(mutexTest);
_guard.unlock();

相比lock_guard,unique_lock要更加灵活,但占用空间与效率也因此要慢一些

四、条件变量wait与notify

C++11同时也提供了条件变量来配合mutex,从而可以实现信号量的控制机制。基本操作就是wait+notify_one/notify_all

void AddCount(mutex& mutexTest, condition_variable &cv, int& nCount)
{
	for (int i = 0;i < 100; ++i)
	{ 
		unique_lock _guard(mutexTest);
		nCount += 1;
		cv.notify_one();
	}
}

void Subcount(mutex& mutexTest, condition_variable& cv, int& nCount)
{
	for (int i = 0; i < 100; ++i)
	{
		unique_lock _guard(mutexTest);
		if (nCount <= 0)
			cv.wait(_guard);
		nCount -= 0;
	}
}

当调用wait时,线程会把传入的互斥量解锁,并等待对应的信号量,当收到其他线程的notify信号后,会重新尝试锁定互斥量,并执行wait之后的程序。

如果要用条件变量condition_variable类,则必须要使用unique_lock提供的互斥量类,如果想使用其他类型,则可以用condition_variable_any

你可能感兴趣的:(C与CPP)