成员变量 | 介绍 |
---|---|
id | 线程id |
native_handle_type | 本地句柄类型 |
成员函数 | 介绍 |
---|---|
(constructor) | 构造线程 |
(destructor) | 构析线程 |
operator= | move-assign thread |
get_id | 获得线程id |
joinable | 检查是否可接合的 |
join | 加入线程 |
detach | 分离线程 |
swap | 交换线程 |
native_handle | 获得本地句柄 |
hardware_concurrency | 检测硬件并发性 |
非成员重载 | 介绍 |
---|---|
swap(thread) | 交换线程 |
线程的创建方法有4种,分别介绍如下。
(1)通过函数指针创建
//1 对于无参数的函数
void func() {}
thread th(func);
//2 对于按值传递参数的函数
void func(int a, int b) {}
thread th(func, 1, 2);
//3 对于按址传递参数的函数
void func(int& a, int& b) {}
int val_a = 1, val_b = 2;
thread th(func, ref(val_a), ref(val_b));
//4 对于按值和按址混合传递参数的函数
void func(int a, int b, int& c) {}
int val_c = 3;
thread th(func, 1, 2, ref(val_c));
(2)通过函数对象创建
#include
#include
using namespace std;
class funcObject
{
public:
funcObject(int a, int b) : a_(a), b_(b) {}
void operator()() const
{
cout << "a_ = " << a_ << ", b_ = " << b_ << endl;
}
private:
int a_;
int b_;
};
int main()
{
//使用函数对象创建方法1
thread t1{ funcObject{1, 11} };
t1.join();
//使用函数对象创建方法2
thread t2(funcObject(2, 22));
t2.join();
//使用函数对象创建方法3
funcObject fo(3, 33);
thread t3(fo);
t3.join();
return 0;
}
(3)通过lambda表示式创建
int a = 1, b = 2;
thread t( [a, b]
{
cout << "a = " << a << ", b = " << b << endl;
}
);
(4)通过成员函数创建
#include
#include
using namespace std;
class MyClass
{
public:
MyClass(int a, int b) : a_(a), b_(b) {}
void func() {cout << "a_ = " << a_ << ", b_ = " << b_ << endl;}
private:
int a_;
int b_;
};
int main()
{
MyClass mc(1, 2);
thread t(&MyClass::func, &mc); //通过成员函数来创建线程
t.join();
return 0;
}
(1)join()
当使用join()
函数时,主线程main()
被阻塞,等待被调线程th
终止,然后主线程main()
回收被调线程th
资源,并继续运行。演示代码如下,
#include
#include
using namespace std;
void func1()
{
int n = 10;
while(n--)
cout << "executing func1()..." << endl;
cout << "fun1() end!" << endl;
}
int main()
{
thread th(func1); //实例化一个线程对象th,该线程开始执行
th.join(); //如果缺少该语句,则主线程会直接结束,即便子线程th没有执行完!
cout << "main() end!" << endl;
return 0;
}
输出结果为,
executing func1()...
executing func1()...
executing func1()...
executing func1()...
executing func1()...
executing func1()...
executing func1()...
executing func1()...
executing func1()...
executing func1()...
fun1() end!
main() end!
(2)detach()
使用detach()
函数会使得线程th
在后台运行,并且主线程main()
不必等待子线程th
执行完后才可以退出,还有,即便主线程main()
退出之后,子线程th
仍旧可在后台运行。示例代码如下,
#include
#include
using namespace std;
void func()
{
while(1)
cout << "func1 is operating a" << endl;
}
int main()
{
thread th(func); //实例化一个线程对象th,该线程开始执行
th.detach();
cout << "main end()!" << endl;
return 0;
}
程序运行结果为,
func1 is operating amain end()!
(3)joinable()
用来判断线程是否可以成功使用join()
或detach()
,返回true
或false
。在join()
或detach()
调用之前,返回true
;在join()
或detach()
调用之后返回false
。示例代码如下,
#include
#include
using namespace std;
void func()
{
int n = 30;
while(n--)
cout << "func1 is operating a" << endl;
}
int main()
{
thread th(func); //实例化一个线程对象th,该线程开始执行
cout << "th.joinable() = " << th.joinable() << endl;
th.join(); //或可以写成th.detach();
cout << "th.joinable() = " << th.joinable() << endl;
cout << "main end()!" << endl;
return 0;
}
程序运行结果为,
func1 is operating ath.joinable() = 1
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
func1 is operating a
th.joinable() = 0
main end()!
(4)get_id()
返回线程id,注意如果返回值为0表示线程对象不可接合。示例代码如下,
#include
#include
using namespace std;
void func1() {}
void func2() {}
int main()
{
thread th1(func1); //实例化一个线程对象th1,该线程开始执行
thread th2(func2); //实例化一个线程对象th2,该线程开始执行
cout << "线程th1的id为:" << th1.get_id() << ",线程th2的id为:" << th2.get_id() << endl;
th1.join();
th2.join();
cout << "线程th1的id为:" << th1.get_id() << ",线程th2的id为:" << th2.get_id() << endl;
cout << "main end()!" << endl;
return 0;
}
程序运行结果为,
线程th1的id为:23720,线程th2的id为:24636
线程th1的id为:0,线程th2的id为:0
main end()!
(5)swap(·) or swap(·,·)
什么情况下会使用到交换线程呢?需要对线程进行排序操作时。
使用方式为
th1.swap(th2);
,表示线程th1
和线程th2
交换。该函数为类的成员函数。std::swap(th1, th2)
,表示线程th1
和线程th2
交换。该函数为非成员函数重载。 注意,使用swap()
函数之前,必须保证线程对象是可接合的,即joinable() == true
,否则会报错!
示例代码如下,
#include
#include
using namespace std;
void func1() {}
void func2() {}
int main()
{
thread th1(func1);
thread th2(func2);
cout << "th1 id = " << th1.get_id() << "; th2 id = " << th2.get_id() << "!" << endl;
th1.swap(th2);
cout << "th1 id = " << th1.get_id() << "; th2 id = " << th2.get_id() << "!" << endl;
swap(th1, th2);
cout << "th1 id = " << th1.get_id() << "; th2 id = " << th2.get_id() << "!" << endl;
th1.join();
th2.join();
return 0;
}
程序输出结果为,
th1 id = 9208; th2 id = 14400!
th1 id = 14400; th2 id = 9208!
th1 id = 9208; th2 id = 14400!
join()
或detach()
报错#include
#include
using namespace std;
void func()
{
cout << "func() is executed!" << endl;
}
int main()
{
thread th(func);
th.join(); //如果注释该行,程序会报错
return 0;
}
原因是什么呢?这个问题与thread
类的构析函数(相关介绍见请点击)有关。thread
类的析构函数中,有如下代码块
~thread() noexcept
{ // clean up
if (joinable())
_STD terminate();
}
也就是说如果线程对象是可接合的,即joinable() == true
,那么将会调用std::terminate()
函数,程序报错。
只有当线程对象是不可接合的,即joinable() == false
,也就是说,
那么该线程对象才可以被安全销毁,也即析构函数可以被成功执行。
互斥量,需要包含头文件#include
,作用是防止不同的线程同时操作同一个共享数据。另一点就是使得线程的执行有了先后顺序,必须等待线程th1
结束之后,线程th2
才会运行。
考虑这样一个问题:有全局变量a
,函数void func1()
和函数void func2()
都需要使用变量a
,在主线程中main()
中启用线程th1
执行函数func1
,启用线程th2
执行函数func2
。分析:由于函数func1
和函数func2
运行在不同线程中的,所以可能会出现两者同时对变量a
进行操作的情况,因此,需要定义一个互斥量变量m
,即mutex m;
,在函数func1
和函数func2
的开始时上锁m.lock()
和结尾时解锁m.unlock()
。示例代码如下,
#include
#include
#include
using namespace std;
int a;
mutex m;
void func1()
{
m.lock();
cout << "operating a" << endl;
m.unlock();
}
void func2()
{
m.lock();
cout << "operating a" << endl;
m.unlock();
}
int main()
{
thread th1(func1); //实例化一个线程对象th1,该线程开始执行
thread th2(func2); //实例化一个线程对象th2,该线程开始执行
th1.join();
th2.join();
cout << "main end()!" << endl;
return 0;
}
程序运行结果为,
operating a
operating a
main end()!
使用m.lock()
和m.unlock()
来保证数据不会被同时访问,这种方法有一定的缺陷。比如,如果在func1
函数中m.lock()
之后函数异常退出,那么由于没有对互斥量m
解锁(即,m.unlock()
),func2
函数将无法访问变量a
,线程th2
被堵塞。如下异常代码所示,
#include
#include
#include
using namespace std;
int a;
mutex m;
void func1()
{
m.lock();
cout << "operating a" << endl;
return; //func1函数在m.unlock()之前退出
m.unlock();
}
void func2()
{
m.lock(); //线程th2被阻塞
cout << "operating a" << endl;
m.unlock();
}
int main()
{
thread th1(func1); //实例化一个线程对象th1,该线程开始执行
thread th2(func2); //实例化一个线程对象th2,该线程开始执行
th1.join();
th2.join();
cout << "main end()!" << endl;
return 0;
}
因此,一般使用lock_guard
或unique_lock
来代替m.lock(); m.unlock();
。
#include
#include
#include
using namespace std;
int a;
mutex m;
void func1()
{
lock_guard<mutex> l(m); //或写成unique_lock l(m);
cout << "operating a" << endl;
return;
}
void func2()
{
lock_guard<mutex> l(m); //或写成unique_lock l(m);
cout << "operating a" << endl;
}
int main()
{
thread th1(func1); //实例化一个线程对象th1,该线程开始执行
thread th2(func2); //实例化一个线程对象th2,该线程开始执行
th1.join();
th2.join();
cout << "main end()!" << endl;
return 0;
}
程序输出为,
operating a
operating a
main end()!