C++11多线程thread
一、线程thread
std::thread
在 #include<thread>
头文件中声明,因此使用 std::thread
时需要包含 #include<thread>
头文件。
1.1、语法
1.1.1、构造函数
(1)默认构造函数。
创建一个空的 thread 执行对象。
thread() _NOEXCEPT
{
// construct with no thread
_Thr_set_null(_Thr);
}
(2)初始化构造函数。
创建std::thread执行对象,该thread对象可被joinable,新产生的线程会调用threadFun函数,该函 数的参数由 args 给出。
template<class Fn,class ... Args>
explicit thread(Fn&& fn,Args&& ... args);
&&表示既可以传入左值也可以传入右值。
(3)拷贝构造函数。
// 如果拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
thread(const thread&) = delete;
(4)move构造函数 。
thread(thread&& x)noexcept
move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached。
示例:
#include <iostream>
#include <thread>
using namespace std;
void thread_func(int &a)
{
cout << "thread_func: a = " << (a += 10) << endl;
}
int main()
{
int x = 10;
thread t1(thread_func, ref(x));
thread t2(move(t1)); // t1 线程失去所有权
thread t3;
t3 = move(t2); // t2 线程失去所有权
// t1.join(); //执行会报错:已放弃 (核心已转储)
t3.join();
cout << "main end: x = " << x << endl;
return 0;
}
执行结果:
thread_func: a = 20
main end: x = 20
1.1.2、主要成员函数
(1)get_id():获取线程ID,返回类型std::thread::id对象。
(2)joinable():判断线程是否可以加入等待。
(3)join():等该线程执行完成后才返回。
(4)detach():
detach调用之后,目标线程就成为了守护线程,驻留后台运行,与之关联的std::thread对象失去对目标线程的关联,无法再通过std::thread对象取得该线程的控制权。当线程主函数执行完之后,线程就结束了,运行时库负责清理与该线程相关的资源。
调用 detach 函数之后:
- *this 不再代表任何的线程执行实例。
- joinable() == false
- get_id() == std::thread::id()
1.2、简单线程的创建
使用std::thread创建线程,提供线程函数或者函数对象,并可以同时指定线程函数的参数。
- 传入0个值
- 传入2个值
- 传入引用
- 传入类函数
- detach
- move
(1)传入0个值:
#include <iostream>
#include <thread>
using namespace std;
void thread_func1()
{
cout << "thread_func1()" << endl;
}
int main()
{
thread t1(&thread_func1); // 只传递函数
t1.join(); // 阻塞等待线程函数执行结束
return 0;
}
(2)传入2个值:
#include <iostream>
#include <thread>
using namespace std;
void thread_func2(int a, int b)
{
cout << "thread_func2(): a + b =" << a + b << endl;
}
int main()
{
int a = 10;
int b = 16;
thread t2(thread_func2, a, b);
t2.join();
return 0;
}
(3)传入引用:
#include <iostream>
#include <thread>
using namespace std;
void thread_func3(int &c)
{
cout << "thread_func3(): &c = " << &c
cout << " --> c + 10 =" << (c += 10) << endl;
}
int main()
{
int c = 10;
thread t3(thread_func3, ref(c));
t3.join();
cout << "main --> 3 : &c = " << &c << ", c = " << c << endl;
return 0;
}
(4)传入类函数:
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
void func4(int a)
{
cout << "thread:" << name_ << ", fun4 a = " << a << endl;
}
void setName(string name)
{
name_ = name;
}
void displayName()
{
cout << "this:" << this << ", name:" << name_ << endl;
}
void play()
{
std::cout << "play call!" << std::endl;
}
private:
string name_;
};
int main()
{
cout << "test--------------------------" << endl;
A *a_ptr = new A();
a_ptr->setName("hello,C++11");
thread t4(A::func4, a_ptr, 10);
t4.join();
delete a_ptr;
A *a_ptr2 = new A();
a_ptr2->setName("hello,C++14");
thread t42(&A::func4, a_ptr2, 10);// 传入类的函数地址、类地址、参数
t42.join();
delete a_ptr;
return 0;
}
最好使用取地址符&的方式传入类函数,避免兼容问题。
(5)detach() :
将子线程从主线程中分离出来,主线程不再具有管理此子线程的能力。
#include <iostream>
#include <thread>
using namespace std;
void thread_func5()
{
cout << "func5 into sleep " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "func5 leave " << endl;
}
int main()
{
thread t5(&thread_func5);
t5.detach();
// t5.join() // 抛出异常
cout << "t5 id : " << t5.get_id() << endl; // 抛出异常
cout << "t5 joinable: " << t5.joinable() << endl;
return 0;
}
执行结果:
t5 id : thread::id of a non-executing thread
t5 joinable: 0
(6)std::move() :
线程所有权转移。
#include <iostream>
#include <thread>
using namespace std;
int main()
{
thread t6(func6);
thread t7(move(t6));
//t6.join(); // 抛出异常
cout << "t6 id : " << t6.get_id() << endl;
cout << "t6 joinable: " << t6.joinable() << endl;
cout << "t7 joinable: " << t7.joinable() << endl;
t7.join();
return 0;
}
执行结果:
t6 id : thread::id of a non-executing thread
t6 joinable: 0
t7 joinable: 1
this is func6 !
1.3、线程封装
封装线程,子类能继承,然后子类能实现具体的业务逻辑。创建线程通过new来实现,参数列表和使用构造函数创建是一样的。
ower_thread.h
#ifndef _OWER_THREAD_H_
#define _OWER_THREAD_H_
#include <thread>
class Ower_Thread {
public:
Ower_Thread(); // 构造函数
virtual ~Ower_Thread(); // 析构函数
bool start();
void stop();
bool isAlive() const; // 线程是否存活
std::thread::id id()
{
return th_->get_id();
}
std::thread* getThread()
{
return th_;
}
void join(); // 等待当前线程结束, 不能在当前线程上调用
void detach(); //能在当前线程上调用
static size_t CURRENT_THREADID();
protected:
void threadEntry();
virtual void run() = 0; // 运行
protected:
bool running_; //是否在运行
std::thread *th_;
};
#endif
ower_thread.cc
#include "ower_thread.h"
#include <sstream>
#include <iostream>
#include <exception>
Ower_Thread::Ower_Thread() :
running_(false), th_(NULL)
{
}
Ower_Thread::~Ower_Thread()
{
if (th_ != NULL)
{
//如果到调用析构函数的时候,调用者还没有调用join则触发detach,此时是一个比较危险的动 作,用户必须知道他在做什么
if (th_->joinable())
{
std::cout << "~Ower_Thread detach"<<std::endl;
th_->detach();
}
delete th_;
th_ = NULL;
}
std::cout << "~Ower_Thread()" << std::endl;
}
bool Ower_Thread::start()
{
if (running_)
{
return false;
}
try
{
th_ = new std::thread(&Ower_Thread::threadEntry, this);
}
catch (...)
{
throw "[Ower_Thread::start] thread start error";
}
return true;
}
void Ower_Thread::stop()
{
running_ = false;
}
bool Ower_Thread::isAlive()const
{
return running_;
}
void Ower_Thread::join()
{
if (th_->joinable())
{
th_->join(); // 不是detach才去join
}
}
void Ower_Thread::detach()
{
th_->detach();
}
size_t Ower_Thread::CURRENT_THREADID()
{
// 声明为thread_local的本地变量在线程中是持续存在的,不同于普通临时变量的生命周期,
// 它具有static变量一样的初始化特征和生命周期,即使它不被声明为static。
static thread_local size_t threadId = 0;
if (threadId == 0)
{
std::stringstream ss;
ss << std::this_thread::get_id();
threadId = strtol(ss.str().c_str(), NULL, 0);
}
return threadId;
}
void Ower_Thread::threadEntry()
{
running_ = true;
try
{
run();
}
catch (std::exception &ex)
{
running_ = false;
throw ex;
}
catch (...)
{
running_ = false;
throw;
}
running_ = false;
}
test.cc
#include <iostream>
#include <chrono>
#include "ower_thread.h"
using namespace std;
class A:public Ower_Thread
{
public:
void run()
{
while (running_)
{
cout << "class A" << endl;
this_thread::sleep_for(chrono::seconds(5));
}
cout << "----- leave class A " << endl;
}
};
class B :public Ower_Thread
{
public:
void run()
{
while (running_)
{
cout << "class B" << endl;
this_thread::sleep_for(chrono::seconds(2));
}
cout << "----- leave class B " << endl;
}
};
int main()
{
A a;
a.start();
B b;
b.start();
this_thread::sleep_for(chrono::seconds(5));
a.stop();
a.join(); // 需要我们自己join
b.stop();
b.join(); // 需要我们自己join
cout << "Hello World!" << endl;
return 0;
}
编译:
g++ -o my_thread test.cc ower_thread.cc -lpthread -std=c++11
执行结果:
$ ./my_thread
class B
class A
class B
class B
class A
class B
class B
----- leave class A
----- leave class B
Hello World!
~Ower_Thread()
~Ower_Thread()
1.4、std::this_thread
此命名空间对一组访问当前线程的函数进行分组。
功能 | 含义 |
---|---|
get_id | 获取线程 ID(函数) |
yield | 让出CPU |
sleep_until | 睡眠到时间点(功能) |
sleep_for | 睡眠时间跨度(功能) |
1.4.1、std::this_thread::get_id()
功能:获取线程 ID。
返回:返回调用线程的线程 ID,此值唯一标识线程。成员类型的对象唯一标识线程thread::id。
示例:
// thread::get_id / this_thread::get_id
#include <iostream> // std::cout
#include <thread> // std::thread, std::thread::id, std::this_thread::get_id
#include <chrono> // std::chrono::seconds
std::thread::id main_thread_id = std::this_thread::get_id();
void is_main_thread() {
if ( main_thread_id == std::this_thread::get_id() )
std::cout << "This is the main thread.\n";
else
std::cout << "This is not the main thread.\n";
}
int main()
{
is_main_thread();
std::thread th (is_main_thread);
th.join();
}
1.4.2、std::this_thread::yield()
调用线程生成,为实现提供了重新调度的机会。当线程等待其他线程前进而不阻塞时,应调用此函数。
示例:
// this_thread::yield example
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::yield
#include <atomic> // std::atomic
std::atomic<bool> ready(false);
void count1m(int id) {
while (!ready) { // wait until main() sets ready...
std::this_thread::yield();
}
for (volatile int i = 0; i<1000000; ++i) {}
std::cout << id;
}
int main()
{
std::thread threads[10];
std::cout << "race of 10 threads that count to 1 million:\n";
for (int i = 0; i<10; ++i) threads[i] = std::thread(count1m, i);
ready = true; // go!
for (auto& th : threads) th.join();
std::cout << '\n';
return 0;
}
1.4.3、std::this_thread::sleep_for
阻止在指定的时间段内执行调用线程。
当前线程的执行将停止,直到至少从现在开始。其他线程继续执行。
参数:
调用线程恢复执行的时间跨度。请注意,多线程管理操作可能会导致超出此范围的某些延迟。是表示特定相对时间的对象。
示例:
#include <iostream> // std::cout, std::endl
#include <thread> // std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
int main()
{
std::cout << "countdown:\n";
for (int i=10; i>0; --i) {
std::cout << i << std::endl;
std::this_thread::sleep_for (std::chrono::seconds(1));
}
std::cout << "Lift off!\n";
return 0;
}
总结
编译时需要添加lpthread库。
本文将深入解析C++11中多线程编程的核心组件——thread的使用方法。通过详细的示例代码和实际场景的案例,帮助读者全面了解和掌握thread的功能和灵活性。文章将从thread的创建、启动和管理等方面展开讲解,同时介绍了如何处理线程间的通信和同步问题,以及如何利用thread提高程序的性能和并发能力。无论你是初学者还是有一定经验的开发者,本文都将帮助你深入理解C++11多线程编程中的thread,为你的代码提供更强大的并发能力和性能优化的可能。