C++中的线程类
C++中的线程类
1)5个头文件(C++11)
- atomic:主要声明了两个类,
std::atomic
和std::atomic_flag
,另外还声明了一套C风格的原子类与C兼容的原子操作的函数。 - thread:主要声明了std::thread类、另外
std::this_thread
命名空间也在该头文件中。 - mutex:主要声明了与互斥锁(mutex)相关的类,包括
std::mutex
系列类、std::lock_guard
、std::unique_lock
以及其他的类型和函数。 - condition_variable:主要声明了与条件变量相关的类,包括
std::condition_variable
和std::condition_variable_any
。 - future:主要声明了
std::promise
、std::package_task
两个Provider类、以及std::future
和std::share_future
两个Future类、另外还有一些与之相关的类型和函数,std::async
函数就声明再次头文件中。
类std::thread的常用成员函数
成员函数 | 说明(public 访问方式) |
---|---|
thread |
构造函数,有4种 |
get_id |
获得线程ID |
joinable |
判断线程对象是否可连接 |
join |
阻塞函数,等待线程结束 |
native_handle |
用于获得与操作系统相关的原生线程句柄(需要本地库支持) |
swap |
线程交换 |
detach |
线程分离 |
2)创建线程
①默认构造函数
thread();
刚定义默认构造函数(不带参数)的thread对象,其线程不会马上运行
//批量创建线程 其每次执行顺序不一样,与CPU的调度有关
#include<stdio.h>
#include<stdlib.h>
#include<chrono> //std::chrono::seconds
#include<iostream>
#include<thread> //std::thread,std::this_thread::sleep_for
void *thfunc(int n) //线程函数
{
std::cout << "thfunc:" << n <<std::endl;
}
int main(int argc, const char *argv[])
{
std::thread threads[5]; //批量定义5个thread对象,但此时并不会运行
std::cout << "create 5 thread...\n";
for(int i = 0; i < 5; i++){
threads[i] = std::thread(thfunc, i+1); //这里开始执行线程函数thfunc
}
for(auto& t : threads){ //等待每个线程结束
t.join();
}
std::cout << "All threads joined.\n";
return EXIT_SUCCESS;
}
②初始化构造函数
template <class FN, class... args>
explicit thread (Fn&& fn, Args&&... args);
fn是线程函数指针,args是可选的,是要传入线程的参数。
成员函数join声明(阻塞函数,等待线程结束):
void join();
这样创建的线程是可连接的,因此线程对象必须在销毁时调用join函数,或者将其设置为可分离。
通过初始化构造函数创建线程:
//创建一个线程,不传参数
#include<iostream>
#include<thread>
#include<unistd.h> //sleep
void thfunc() //线程函数
{
std::cout << "I am C++11 thread func" << std::endl;
}
int main(int argc, char *argv[])
{
std::thread t(thfunc); //定义线程对象,并把函数指针传入
sleep(1); //main线程挂起一秒钟,为了让子线程有机会执行
return 0;
}
//创建一个线程,并传入整形参数
#include<iostream>
#include<thread>
void thfunc(int n) //线程函数
{
std::cout << "thfunc:" << n << std::endl;
}
int main(int argc, char *argv[])
{
std::thread t(thfunc, 1); //定义线程对象t,并把线程函数指针和线程函数传入
t.join(); //等待线程对象t结束
return 0;
}
//创建一个线程,并传入结构体作为参数
#include<iostream>
#include<thread>
typedef struct
{
int n;
const char *str; //注意这里要有const,否则会有警告
}MYSTRUCT;
void thfunc(void *arg) //线程函数
{
MYSTRUCT *p = (MYSTRUCT*)arg;
std::cout << "in thfunc:n=" << p->n << ",str=" << p->str << std::endl;
}
int main(int argc, char *argv[])
{
MYSTRUCT mystruct; //定义结构体
//初始化结构体
mystruct.n = 110;
mystruct.str = "hello world";
std::thread t(thfunc, &mystruct); //定义线程对象t,并把结构体变量的地址传入
t.join(); //等待线程对象t结束
return 0;
}
//创建一个线程,传多个参数
#include<iostream>
#include<thread>
void thfunc(int n, int m, int *pk, char s[]) //线程函数
{
std::cout << "in thfunc:n=" << n << ",m=" << m << ",k=" << *pk << "\nstr=" << s << std::endl;
*pk = 5000; //修改* pk
}
int main(int argc, char *argv[])
{
int n = 110, m = 200, k = 5;
char str[] = "hello world";
std::thread t(thfunc, n,m,&k,str); //定义线程对象t,并传入多个函数
t.join(); //等待线程对象t结束
std::cout << "k=" << k << std::endl; //此时应打印5000
return 0;
}
//把可连接线程转为分离线程(C++ and POSIX)
#include<iostream>
#include<thread>
void thfunc(int n, int m, int *k, char s[]) //线程函数
{
std::cout << "in thfunc:n=" << n << ",m=" << m << ",k=" << *k << "\nstr=" << s << std::endl;
*k = 5000; //修改* pk
}
int main(int argc, char *argv[])
{
int n = 110, m = 200, k = 5;
char str[] = "hello world";
std::thread t(thfunc, n,m,&k,str); //定义线程对象t
t.detach(); //分离线程
std::cout << "k=" << k << std::endl; //此时应打印3
pthread_exit(NULL); //mian线程结束,但进程并不会结束,下面一句不会执行
std::cout << "this line will not run" << std::endl;
return 0;
}
3)移动(move)构造函数
通过向thread构造函数中传入一个C++对象来创建线程
thread (thread&& x);
调用成功后,x不代表任何thread对象
#include<iostream>
#include<thread>
void fun(int& n) //线程函数
{
std::cout << "fun: " << n << std::endl;
n += 20;
std::this_thread::sleep_for(std::chrono::milliseconds(10)); //等待10毫秒
}
int main()
{
int n = 0;
std::cout << "n = " << n << std::endl;
n = 10;
std::thread t1(fun, std::ref(n)); //ref(n)是取n的引用
std::thread t2(move(t1));
t2.join(); //等待t2执行完毕
std::cout << "n = " << n << std::endl;
return 0;
}
执行结果:
n = 0
fun: 10
n = 30注意:t1并不会执行,执行的是t2,因为t1的线程函数移动给了t2
4)线程标识符
概念:
线程标识符(ID)可以用来标识某个thread对象所对应的线程,用来区分不同的线程
类thread提供了成员函数getid
来获取线程ID,该函数声明:
thread::id get_id()
id是线程标识符的类型,它是类thread的成员,用来唯一标识某个线程
①线程比较:
#include<iostream>
#include<thread>
using namespace std;
thread::id main_thread_id = this_thread::get_id(); //获取主线程id
void is_main_thread()
{
if(main_thread_id == this_thread::get_id()){ //判断是否和主线程id相同
cout << "This is the mian thread." << endl;
}
else{
cout << "This is not the main thread." << endl;
}
}
int main()
{
is_main_thread(); //is_main_thread作为mian线程的普通函数调用
thread th(is_main_thread); //is_main_thread作为线程函数使用
th.join(); //等待th结束
return 0;
}
5)当前线程this_thread
①让出cpu时间
void yied();
调用该函数的线程放弃执行,回到就绪态
//创建10个线程,每个线程中让一个变量从1累加到100,谁先完成就打印它的编号,以此排名。为了公平起见,创建线程的时候,先不让它们占用CPU时间,一直到main线程改变全局变量值,各个子线程才一起开始累加
#include<iostream>
#include<thread>
#include<atomic>
using namespace std;
atomic<bool> ready(false); //定义全局变量
void thfunc(int id)
{
while (!ready){ //一直等待,直到mian线程中重置全局变量ready
this_thread::yield();
}
for(volatile int i = 0; i < 100; ++i)
{}
cout << id << ","; //累加完毕后,打印本线程编号
}
int main()
{
thread threads[10]; //定义10个线程对象
cout << "race or 10 threads that count to 1 100" << endl;
for(int i = 0; i < 10; ++i){
threads[i] = thread(thfunc, i);
}
ready = true;
for(auto& th : threads) th.join(); //等待10个线程全部结束
cout << endl;
return 0;
}
②暂停线程
命名空间this_thread
还有两个函数用来阻塞线程,暂停执行一段时间:
sleep_until
template <class Clock, class Duration>
void sleep_until (const chrono::time_point<Clock, Duration>& abs_time);
参数abs_time表示函数阻塞线程到abs_time时间点,之后再执行
sleep_for
template <class Rep, class Period>
void sleep_for (const chrono::duration<Rep, Period>& ret_time);
参数rel_time表示线程挂起的时间段,在这段时间内线程暂停执行
//暂停线程到下一分钟
#include<iostream>
#include<thread>
#include<chrono>
#include<ctime>
#include<time.h>
#include<stddef.h>
using namespace std;
void getNowTime() //获取并打印当前时间
{
timespec time;
struct tm nowTime;
clock_gettime(CLOCK_REALTIME, &time); //获取相对于1970到现在的秒数
localtime_r(&time.tv_sec, &nowTime);
char current[1024];
printf(
"%04d-%02d-%02d %02d:%02d:%02d\n",
nowTime.tm_year + 1900,
nowTime.tm_mon + 1,
nowTime.tm_mday,
nowTime.tm_hour,
nowTime.tm_min,
nowTime.tm_sec);
}
int main()
{
using std::chrono::system_clock;
std::time_t tt = system_clock::to_time_t(system_clock::now());
struct std::tm* ptm = std::localtime(&tt);
getNowTime(); //打印当前时间
cout << "Waiting for the next minute to begin...\n";
++ptm->tm_min; //累加一分钟
ptm->tm_sec = 0; //秒数置0
this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
//暂停执行到下一个整分时间
getNowTime(); //打印当前时间
return 0;
}
//暂停线程5秒
#include<iostream>
#include<thread>
#include<chrono>
using namespace std;
int main()
{
cout << "countdown:" << endl;
for(int i = 5; i > 0; --i){
cout << i << endl;
this_thread::sleep_for(chrono::seconds(1)); //暂停1秒
}
cout << "Lift off!" << endl;
return 0;
}