目录
一、创建线程
(1)简单的栗子:
(2)创建一个执行有参函数的线程:
(3)类成员函数的线程:
二、关闭线程
(1)加入式(join())
(2)分离式(detach())
三、获取线程ID
四、转移线程所有权
五、进程与线程
c++11提供了一个新的头文件<thread>提供了对线程函数的支持的声明
原型:
- template<class Fn,class ...Args>
- explicit thread(Fn &&fn,Args&&... args); //这是thread类的构造函数
thread是一种数据类型,可用来实例化对象,每个对象都是一个线程
参数可接受任意的可调用对象类型(带参数或者不带参数),包括lambda表达式(带变量捕获或者不带),函数,函数对象,以及函数指针。
一、创建线程
(1)简单的栗子:
#include <iostream>
#include <thread>
void test()
{
std::cout << "hello world!" << std::endl;
}
int main()
{
//hello函数会在新的线程中执行
std::thread t(test); //test是无参的
//join会在调用线程等待std::thread对象相关联的线程结束
t.join();
system("pause"); //msvc
return 0;
}
(2)创建一个执行有参函数的线程:
#include <iostream>
#include <string>
#include <thread>
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<void(*)()>(hello));
std::thread t2(static_cast<void(*)(std::string)>(hello), "gongjianbo"); 由thread的原型,t2的第二个参数将传递给第一个参数,
t1.join();
t2.join();
system("pause"); //msvc
return 0;
}
(3)类成员函数的线程:
#include <iostream>
#include <string>
#include <thread>
#include <functional>
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<void(MyClass::*)(std::string)>(&MyClass::test2),&obj,"gongjianbo");
t1.join();
t2.join();
t3.join();
t4.join();
system("pause"); //msvc
return 0;
}
二、关闭线程
thread库给我们两种选择:1.加入式(join()) 2.分离式(detach())。值得一提的是,你必须在thread
对象销毁之前做出选择,这是因为线程可能在你加入或分离线程之前,就已经结束了,之后如果再去分离它,线程可能会在thread
对象销毁之后继续运行下去。
成员函数joinable用于检测thread是否关闭。joinable为false的情况有3种:1.thread调用过detach; 2.thread已经join过; 3.thread为空。
(1)加入式(join())
即彻底关闭线程,一个线程的对象每次你只能使用一次join(),当你调用的join()之后joinable()就将返回false了
join用于阻塞当前线程等待thread执行完执行体。一般用于多线程之间进行同步。对于不具备分离属性的线程必须要join,否则会导致terminal。
#include <iostream>
#include <thread>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t(foo);
std::cout << "before joining,joinable=" << std::boolalpha << t.joinable() << std::endl;
t.join();
std::cout << "after joining, joinable=" << std::boolalpha << t.joinable() << '\n';
}
***************
[thread]main
before joining,joinable=true
after joining, joinable=false
(2)分离式(detach())
把线程移交给后台运行,执行后调用joinable()也是返回false
分离的线程会在后台运行,其所有权(ownership)和控制权将会交给c++运行库。同时,C++运行库保证,当线程退出时,其相关资源的能够正确的回收。
三、获取线程ID
成员函数get_id用于返回线程的句柄。get_id可能返回joinable为true的thread的id,joinable为false则返回 id().
#include <iostream>
#include <thread>
void fun()
{
std::cout << std::this_thread::get_id() << std::endl;
}
int main()
{
std::thread t(fun);
std::cout << t.get_id() << std::endl;
t.join();
}
四、转移线程所有权
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()终止程序
五、进程与线程
- 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位(线程不拥有系统资源,但可以访问隶属进程的系统资源)。
- 一个进程可以有一个或多个线程
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),各个线程之间有独立的寄存器,栈等。
- 线程上下文切换比进程上下文切换快得多(切换进程涉及CPU环境的设置,切换线程只需要保存和设置一些寄存器内容,并且进程创建和撤销时,在内存空间、IO设备等分配上都会有很大的开销)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)