为了提高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()
接着来示范一些简单的线程实例操作
使用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
同步机制(Synchronized)有以下几种:互斥量(Mutex),讯号量(Semaphore),条件变量(Condition Variable),原子变量(Atomic),队列(Queue),事件(Event)
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
当同时有几个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
采用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;
}
独占所有权的方式对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;
}
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
用于等待的同步机制,能阻塞一个或多个线程,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))
在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