C++中的线程类

C++中的线程类

1)5个头文件(C++11)

  • atomic:主要声明了两个类,std::atomicstd::atomic_flag,另外还声明了一套C风格的原子类与C兼容的原子操作的函数。
  • thread:主要声明了std::thread类、另外std::this_thread命名空间也在该头文件中。
  • mutex:主要声明了与互斥锁(mutex)相关的类,包括std::mutex系列类、std::lock_guardstd::unique_lock以及其他的类型和函数。
  • condition_variable:主要声明了与条件变量相关的类,包括std::condition_variablestd::condition_variable_any
  • future:主要声明了std::promisestd::package_task两个Provider类、以及std::futurestd::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;
}

posted @ 2023-08-22 17:56  洋綮  阅读(143)  评论(0)    收藏  举报