C++ std::thread初步了解

0.前言

C++标准库从C++11开始提供了线程的支持,本文为《C++并发编程实战》第二章的学习笔记。

1.启动线程

先从一个简单的例子开始:

#include 
#include 

void test()
{
	std::cout << "hello world!" << std::endl;
}

int main()
{
	//hello函数会在新的线程中执行
	std::thread t(test);
	//join会在调用线程等待std::thread对象相关联的线程结束 
	t.join();

	system("pause"); //msvc
	return 0;
}

使用C++线程库来开始一个线程需要构造一个std::thread对象,std::thread可以与任何可调用类型一同工作。一旦开启了线程,需要显式的决定是等待其结束(join)还是让他在后台运行(detach),如果在std::thread对象销毁前还未作决定,那么程序会被终止(std::thread的析构函数调用std::terminate())。调用detach后,没有直接的方法与之通信,也不能再join等待他结束。detach分离的线程所有权和控制权被转交给C++运行时库,以确保与线程相关的资源能被正确回收。

2.参数传递以及调用重载的函数

先看看普通函数的调用方式:

#include 
#include 
#include 

void hello()
{
	std::cout << "hello world!" << std::endl;
}

void hello(std::string name)
{
	std::cout << "hello " << name << std::endl;
}

int main()
{
	//因为重载了,所以这里用static_cast转换一下
	std::thread t1(static_cast(hello));
	std::thread t2(static_cast(hello), "gongjianbo");
	t1.join();
	t2.join();

	system("pause"); //msvc
	return 0;
}

对于类成员函数,重载可以借用std::bind,lambda,static_cast等来调用: 

#include 
#include 
#include 
#include 

class MyClass
{
public:
	void test1() {
		std::cout << "test1 method" << std::endl;
	}
	void test2() {
		std::cout << "test2" << std::endl;
	}
	void test2(int i) {
		std::cout << "test2 " << i << std::endl;
	}
	void test2(std::string str) {
		std::cout << "test2 " << str << std::endl;
	}
};

int main()
{
	MyClass obj;
	std::thread t(&MyClass::test1, &obj);
	t.join();

	using Func = void(MyClass::*)();
	Func f1 = &MyClass::test2;
	std::thread t1(std::bind(f1, obj));  //obj->test2()
	void(MyClass::*f2)(int) = &MyClass::test2;
	std::thread t2(std::bind(f2, obj, 1992));//obj->test2(int)

	std::thread t3([&obj]() { obj.test2(1993); });//obj->test2()

	std::thread t4(static_cast(&MyClass::test2),&obj,"gongjianbo");

	t1.join();
	t2.join();
	t3.join();
	t4.join();

	system("pause"); //msvc
	return 0;
}

输出如下(因为并行执行,所以顺序是乱的):

C++ std::thread初步了解_第1张图片

传递参数的引用,可以借助std::ref来实现。默认情况下,std::thread无视函数所期望的类型,并且盲目地复制了所提供的值,对于引用参数,他将传递一个副本的引用而非该对象本身的引用。

void method(int i,type & data);
...
type data;
std::thread t(method,1992,std::ref(data));

3.转移线程所有权

std::thread的实例是可移动(movable)但不可复制的(copyable),在任意时刻只有一个对象与某个特定的执行线程相关联,并且不能通过向管理一个线程的std::thread对象赋新值来舍弃之前的关联线程。

	std::thread t1(test); 
	std::thread t2 = std::move(t1);//t1关联线程被转移到t2
	t1 = std::thread(test); //t1与一个新的线程相关联
	//不能通过向管理一个线程的std::thread对象赋新值来舍弃之前的关联线程
	t1 = std::move(t2); //t1已关联一个线程,再赋值会调用std::terminate()终止程序

4.其他

并发运行的线程数量指示:std::thread::hardware_concurrency(),在多核cpu上它可能是cpu核心的数量,如果信息不可用可能会返回0。

std::cout << "hardware_currency:" << std::thread::hardware_concurrency() << std::endl;
//我的电脑八核的,输出8

线程标识:std::thread::id,通过std::thread::get_id()获取与之关联的线程id,用std::this_thread::get_id()获取当前线程id。如果两个线程id比较结果相等,则是同一个线程,或都没有关联线程。

	std::thread::id main_thread_id;//没有关联线程,0
	std::thread t(test);
	std::cout << "id:" << main_thread_id 
		<< " " <

不出意外,会打印三个不同的id值。 

5.线程与进程

最后,了解下线程和进程的区别:(复制粘贴的)

1.线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位(线程不拥有系统资源,但可以访问隶属进程的系统资源)。
2.一个进程可以有一个或多个线程
3.进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),各个线程之间有独立的寄存器,栈等。
4.线程上下文切换比进程上下文切换快得多(切换进程涉及CPU环境的设置,切换线程只需要保存和设置一些寄存器内容,并且进程创建和撤销时,在内存空间、IO设备等分配上都会有很大的开销)。

你可能感兴趣的:(C++,没有结局的开始)