C++11多线程thread使用详解
admin
2024-01-31 03:48:41
0

C++11多线程thread

  • 一、线程thread
    • 1.1、语法
      • 1.1.1、构造函数
      • 1.1.2、主要成员函数
    • 1.2、简单线程的创建
    • 1.3、线程封装
    • 1.4、std::this_thread
      • 1.4.1、std::this_thread::get_id()
      • 1.4.2、std::this_thread::yield()
      • 1.4.3、std::this_thread::sleep_for
  • 总结
  • 后言

一、线程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
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 
#include 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 函数之后:

  1. *this 不再代表任何的线程执行实例。
  2. joinable() == false
  3. get_id() == std::thread::id()

1.2、简单线程的创建

使用std::thread创建线程,提供线程函数或者函数对象,并可以同时指定线程函数的参数。

  1. 传入0个值
  2. 传入2个值
  3. 传入引用
  4. 传入类函数
  5. detach
  6. move

(1)传入0个值:

#include 
#include using namespace std;void thread_func1()
{cout << "thread_func1()" << endl;
}int main()
{thread t1(&thread_func1);	// 只传递函数t1.join();					// 阻塞等待线程函数执行结束return 0;
}

(2)传入2个值:

#include 
#include 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 
#include 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 
#include 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 
#include 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 
#include 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 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 
#include 
#include 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"<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 
#include 
#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(); // 需要我们自己joinb.stop();b.join(); // 需要我们自己joincout << "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        // std::cout
#include          // std::thread, std::thread::id, std::this_thread::get_id
#include          // std::chrono::secondsstd::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";elsestd::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        // std::cout
#include          // std::thread, std::this_thread::yield
#include          // std::atomicstd::atomic 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        // std::cout, std::endl
#include          // std::this_thread::sleep_for
#include          // std::chrono::secondsint 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/c++linux系统提升感兴趣的读者,可以点击链接,详细查看详细的服务:C/C++服务器课程

相关内容

热门资讯

安卓用的是什么系统,深度解析A... 你有没有想过,你的安卓手机里到底装的是什么系统呢?是不是觉得这个问题有点儿深奥,其实它背后隐藏着手机...
安卓系统如何改定位苹果,安卓系... 你有没有想过,你的安卓手机竟然可以模仿苹果的定位功能?没错,就是那种精准到让你仿佛置身于苹果用户世界...
安卓系统不能删除软件吗,为何无... 你是不是也遇到过这种情况:手机里装了好多软件,可就是找不到删除它们的办法?别急,今天就来聊聊这个让人...
鸿蒙系统属于安卓么,揭秘其与安... 你有没有想过,手机里的那个鸿蒙系统,它到底是不是安卓的“亲戚”呢?这个问题,估计不少手机控都在心里默...
安卓大屏导航恢复系统,重塑流畅... 你有没有发现,手机屏幕越来越大,导航系统也越来越复杂?有时候,我们不小心把安卓大屏导航恢复系统给误删...
国产安卓机哪个系统好用,探索好... 你有没有想过,国产安卓机哪个系统最好用呢?这可是个让人纠结的问题,毕竟每个系统都有它的特色和亮点。今...
安卓11系统操作方法,轻松上手... 你升级到安卓11系统了吗?是不是还在为如何更好地操作这个新系统而烦恼呢?别急,今天我就来给你详细介绍...
安卓2.3系统流畅内存,揭秘内... 你有没有想过,为什么有些老手机还能跑得飞快,而有些新手机却卡得要命?今天,咱们就来聊聊这个话题——安...
支持安卓系统的硬件芯片,引领智... 你有没有发现,现在手机的世界里,支持安卓系统的硬件芯片可是越来越厉害了呢!想象那些小小的芯片,就像魔...
手机安卓系统性价比,性能与价格... 你有没有发现,现在手机市场上安卓系统的手机越来越受欢迎了呢?这不,最近我入手了一款性价比超高的安卓手...
群晖vmm安装安卓系统,群晖V... 亲爱的电脑爱好者们,你是否曾梦想过在群晖NAS上安装安卓系统,享受移动设备的便捷与NAS的强大存储?...
免费的美发会员安卓系统,安卓系... 亲爱的读者们,你是否曾在街头巷尾,被那些免费的美发会员卡吸引得心痒痒?想象不用花一分钱,就能享受到专...
ego华为装安卓系统,开启智能... 你知道吗?最近科技圈可是炸开了锅,因为华为这个巨头竟然要给自家手机装上安卓系统!这可不是什么小打小闹...
安卓系统软件数据,揭秘智能设备... 你知道吗?在这个科技飞速发展的时代,安卓系统软件数据可是个让人又爱又恨的小家伙。它就像一个调皮的精灵...
安卓系统啥文档不能删,安卓系统... 你有没有想过,在你的安卓手机里,有些文档就像是顽皮的小精灵,无论你怎么赶,它们就是赖在那里不肯走呢?...
安卓游戏系统怎么转苹果,安卓游... 你是不是也和我一样,手里拿着安卓手机玩得乐不思蜀,突然有一天,发现了一款超级酷炫的苹果游戏,心痒痒地...
安卓能否刷华为系统版本,揭秘华... 你有没有想过,你的安卓手机能不能刷上华为的系统版本呢?这可是个让人好奇不已的话题呢!想象如果你的安卓...
谷歌最新系统安卓,谷歌带来的颠... 哇,你知道吗?最近谷歌又放大招了!他们推出了全新的操作系统——安卓!这可不是一个小小的更新,而是一次...
鸿蒙系统真的不算安卓吗,引领全... 最近手机圈子里可是热闹非凡呢!尤其是华为的鸿蒙系统,自从发布以来,就一直是大家讨论的焦点。有人说,鸿...
电脑整盘安装安卓系统 你有没有想过,你的电脑除了装Windows系统,还能来个变身大法,变成一个安卓小怪兽呢?没错,今天就...