C++多线程

1.基础知识

为了提高CPU的使用率,将某些需要耗时较多的任务或是大量IO操作(IO处理速度很慢),采用多线程可以适当的提高程序的执行效率。

C++11之后有了std::thread库,需要引入头文件:
先来介绍Thread的成员函数:

# 用来查看当前线程的id
thread::get_id()
# 检查此线程是否还和主线程连接(已经完成join、detach的线程都是false)
thread::joinable()
# 将线程与主线程的连接切断,并且此线程会继续独立执行下去,直到执行结束时候释放分配的资源
thread::detach()
# 交换两个线程
thread::swap()
#############################################################
std::this_thread 命名空间(Namespace)
# 查看当前线程的id
this_thread::get_id()
# 暂时中断此线程,OS会调用其他线程执行
this_thread::yield()
# 设定一个时间,让此线程在指定的时刻后再继续执行
this_thread::sleep_until()
# 暂时中断此线程,等待指定的一段时间后才会被执行
this_thread::sleep_for()

接着来示范一些简单的线程实例操作

  • 建立线程、等待指定线程结束
    使用thread<线程名称>()建立一个线程,若要传入参数,可以在function后加入第二个参数。

使用join()将将主线程暂停,等待指定的线程结束,主线程才会结束

#include 
#include 

void first_thread_job()
{
	cout << "This is the first thread"<<endl;
}
// 传入string x
void second_thread_job(string x)
{
	cout<<"This is the second thread"<<x<<endl;
}

int main()
{
	//建立线程
	thread first_thread(first_thread_job);
	thread second_thread(second_thread_job, "abc");
	
	// 将主程序暂停,等待指定的线程结束
	first_thread.join();
	second_thread.join();
	
	return 0;
}
// =======output========
// This is the first thread
// This is the second thread abc

2.同步机制(Synchronized)

同步机制(Synchronized)有以下几种:互斥量(Mutex),讯号量(Semaphore),条件变量(Condition Variable),原子变量(Atomic),队列(Queue),事件(Event)

2.1 Queue

Thread无法回传值,所以要使用queue.push()将要传回的值存入queue,再用queue.pop()取出

#include 
#include 
#include 

using namespace std;
queue<int> q1;
queue<int>::size_type q1_size;

void first_thread_job(int x)
{
	// 将元素放入queue
	q1.push(x);
	cout<<"This is the first thread"<<x<<endl;
}
int main()
{
	thread first_thread(first_thread_job, 2);
	first_thread.join()
	
	//传回队列的第一个元素,并没有将此元素剔除队列
	int a = q1.front();
	cout<<"The first element is"<<a<<endl;
	q1_size = q1.size();
	cout<<"The queue1 length is"<<q1_size<<endl;
	
	//弹出队列的第一个元素
	q1.pop();
	q1_size = q1.size();
	cout << "The queue2 length is "<<q1_size<<endl;
	return 0;
}
// ========output========
// This is the first thread 2
// The first element is 2
// The queue1 length is 1
// The queue2 length is 0

2.2 Lock

当同时有几个Thread要用到同一个数据时候,为了不发生Race Condition的现象,需要使用lock()以及unlock()来将其锁定住,不让其他Thread执行,C++需要引入头文件

#include 
#include 
#include 

using namespace std;
//定义lock
mutex mu;

void first_thread_job()
{
	mu.lock();
	cout<<"This is the first thread"<<endl;
	mu.unlock();
}
int main()
{
	thread first_thread(first_thread_job);
	first_thread.join();
	return 0;
}

除了Lock之外,mutex还提供了lock_guard以及unique_lock

2.3 lock_guard

采用RAII方法来对mutex对象进行自动加锁、解锁的动作,可以保证线程的安全

#incldue 
#include 
#include 

using namespace std;
mutex g_mutex;

void first_thread_job()
{
	lock_guard<mutex> lock(g_mutex);
	cout<<"This is the firdt thread"<<endl;
}
int main()
{
	thread first_thread(first_thread_job);
	first_thread.join();
	return 0;
}

2.4 unique_lock

独占所有权的方式对mutex对象进行自动加锁、解锁的动作,具有lock_guard的功能,而且更灵活,支持移动赋值的动作,还支持同时锁定多个mutex,但所花费的时间和内存更多,因此如果是lock_guard可以处理的线程,会尽量使用lock_guard。

unique_lock写法跟lock_guard类似

#incldue <iostream>
#include 
#include 

using namespace std;
mutex u_mutex;

void first_thread_job()
{
	unique_lock<mutex> lock(u_mutex);
	cout<<"This is the firdt thread"<<endl;
}
int main()
{
	thread first_thread(first_thread_job);
	first_thread.join();
	return 0;
}

2.5 Semaphore

mutex的扩充版本,可以允许多个线程同时执行,以下是Semaphore的基本代码

# 初始化Semaphore
sem_init(sem_t *sem, int pshared, unsigned int value);
# 定义Semaphore的名称
sem_t *sem
# 设定为0,表示仅供目前的process及其Thread使用。非0表示此Semaphore与其他process共用
int pshared
# 设定Semaphore计数器
unsigned int value
# 用来阻塞该线程,直到Semaphore的值大于0,若解除阻塞后,Semaphore的值会减1,表示可执行的次数减1
sem_wait(sem_t*sem)
# 当有线程阻塞在信号上,调用此函数会使其中一个线程解除阻塞,此时Semaphore的值加1
sem_post(sem_t *sem);
# 删除Semaphore
sem_destroy(sem_t*sem);

接下来示范使用Semaphore

#include 
#include 
#include 

using namespace std;
sem_t binSem;
int a;

void first_thread_job()
{
	sem_wait(&binSem);
	for(int i = 0;i<3;i++)
	{
		a += 1;
		cout << "This is the first thread"<<a<<endl;
	}
}

void second_thread_job()
{
	for(int i=0;i<3;i++)
	{
		a -= 1;
		cout << "This is the second thread"<<a<<endl;
	}
	sem_post(&binSem);
}

int main()
{
	int res;
	//Semaphore初始化
	res = sem_init(&binSem, 0, 0);
	//建立线程
	thread first_thread(first_thread_job);
	thread second_thread(second_thread_job);
	// 将主线程暂停,等待指定的线程结束
	first_thread.join();
	second_thread.join();
	return 0;
}

// =======output======
// first_thread_job 被阻塞,直到second_thread_job把信号加1,才开始执行
// This is the second thread -1
// This is the second thread -2
// This is the second thread -3
// This is the first thread -2
// This is the first thread -1
// This is the first thread 0

2.6 Condition Variable

用于等待的同步机制,能阻塞一个或多个线程,condition_variable提供wait()将线程停下来等待通知,直到接收到另一个线程发出的通知才会被唤醒,提供notify_one()或notify_all()两种函式。

notify_one()只会通知其中一个正在等待的线程,notify_all()则会通知所有正在等待的线程,需要和mutex配合使用。

其中wait()可以有两个参数,第一个参数是mutex,第二个参数是bool类型,当为true时候,线程才会停止等待,如果为false,线程则会等待下一次的通知

#include 
#include 
#include 
#include
using namespace std;
condition_variable cond_var;
mutex u_mutex;
bool ready = false;
void first_thread_job()
{
	 unique_lock<mutex> lock(u_mutex);
	// 使用 wait() 进行等待
	 cout << "thread wait" << endl;
	 cond_var.wait(lock);
	 cout << "This is the first thread " << endl;
}
void second_thread_job()
{
	 unique_lock<mutex> lock(u_mutex);
	// 使用 wait() 进行等待 传入第二个参数
	 cout << "thread wait" << endl;
	 cond_var.wait(lock, [](){ return ready; });
	 cout << "This is the second thread " << endl;
}
int main()
{
 thread first_thread(first_thread_job);
 thread second_thread(second_thread_job);
 cout << "wait 5 millisecond…" << endl;
 this_thread::sleep_for(std::chrono::milliseconds(5));
// 使用 notify_one() 唤醒线程
 cout << "thread notify_one" << endl;
 cond_var.notify_one();
 // 回传ready判断是否要停止等待
 ready = true;
// 使用 notify_one() 唤醒线程
 cout << "thread notify_one" << endl;
 cond_var.notify_one();
 first_thread.join();
 second_thread.join();
 return 0;
}

除了wait()以外,还有提供wait_for()和wait_until()这两个函数,限制等待的时间。wait_for()需要指定给定长度的时间,wait_until()则是需要指定一个时间点,时间的形式要使用STL chrono

cond_var.wait_for(lock, chrono::seconds(5))
cond_var.wait_until(lock, chrono::system_clock::now()+chrono::seconds(5))

2.7 Atomic

在C++11中引入原子操作的概念,提供更简单的机制确保线程的安全与存取共享变量的正确性,在任意时刻只有一个线程能存取这个资源,有点类似于互斥的保护机制,但原子操作比锁的使用效率更高。

#include 
#include 
#include 
using namespace std;

//定义原子变量
atomic_int atomic_a(0);

void first_thread_job()
{
	for(int i=0;i<3;i++)
	{
		atomic_a += 1;
		cout << "This is the first thread "<< atomic_a << endl;
	}
}
int main()
{
 // 建立執行緒
 thread first_thread(first_thread_job);
 first_thread.join();
 return 0;
}
// ====== output ======
// This is the first thread 1
// This is the first thread 2
// This is the first thread 3

参考目录

https://medium.com/ching-i/%E5%A4%9A%E5%9F%B7%E8%A1%8C%E7%B7%92-c-thread-9f6e37c7cf32

你可能感兴趣的:(C++基础知识,线程,C++,线程)