• C++11多线程thread使用详解


    一、线程thread

    std::thread#include头文件中声明,因此使用 std::thread 时需要包含 #include头文件。

    1.1、语法

    1.1.1、构造函数

    (1)默认构造函数
    创建一个空的 thread 执行对象。

    thread() _NOEXCEPT
    {
    	// construct with no thread
    	_Thr_set_null(_Thr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)初始化构造函数。
    创建std::thread执行对象,该thread对象可被joinable,新产生的线程会调用threadFun函数,该函 数的参数由 args 给出。

    template<class Fn,class ... Args>
    explicit thread(Fn&& fn,Args&& ... args);
    
    • 1
    • 2

    &&表示既可以传入左值也可以传入右值。

    (3)拷贝构造函数

    // 如果拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。 
    thread(const thread&) = delete;
    
    • 1
    • 2

    (4)move构造函数 。

    thread(thread&& x)noexcept
    
    • 1

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    执行结果:

    thread_func: a = 20
    main end: x = 20
    
    
    • 1
    • 2
    • 3

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    (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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    最好使用取地址符&的方式传入类函数,避免兼容问题。

    (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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    执行结果:

    t5 id : thread::id of a non-executing thread
    t5 joinable: 0
    
    
    • 1
    • 2
    • 3

    (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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行结果:

    t6 id : thread::id of a non-executing thread
    t6 joinable: 0
    t7 joinable: 1
    this is func6 !
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    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"<<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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    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(); // 需要我们自己join
    	b.stop();
    	b.join(); // 需要我们自己join
    
    	cout << "Hello World!" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    编译:

    g++ -o my_thread test.cc ower_thread.cc -lpthread -std=c++11
    
    
    • 1
    • 2

    执行结果:

    $ ./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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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::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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.4.2、std::this_thread::yield()

    调用线程生成,为实现提供了重新调度的机会。当线程等待其他线程前进而不阻塞时,应调用此函数。
    示例:

    // this_thread::yield example
    #include        // std::cout
    #include          // std::thread, std::this_thread::yield
    #include          // 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    1.4.3、std::this_thread::sleep_for

    阻止在指定的时间段内执行调用线程。
    当前线程的执行将停止,直到至少从现在开始。其他线程继续执行。

    参数:
    调用线程恢复执行的时间跨度。请注意,多线程管理操作可能会导致超出此范围的某些延迟。是表示特定相对时间的对象。

    示例:

    #include        // std::cout, std::endl
    #include          // std::this_thread::sleep_for
    #include          // 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    总结

    编译时需要添加lpthread库。

    本文将深入解析C++11中多线程编程的核心组件——thread的使用方法。通过详细的示例代码和实际场景的案例,帮助读者全面了解和掌握thread的功能和灵活性。文章将从thread的创建、启动和管理等方面展开讲解,同时介绍了如何处理线程间的通信和同步问题,以及如何利用thread提高程序的性能和并发能力。无论你是初学者还是有一定经验的开发者,本文都将帮助你深入理解C++11多线程编程中的thread,为你的代码提供更强大的并发能力和性能优化的可能。

    在这里插入图片描述

  • 相关阅读:
    24-Vue之过滤器基本用法
    第二章第二十二节:文件操作1
    HarmonyOS 实战项目
    海水淡化除硼、饮用水除硼,超纯水除硼、废水除硼
    2023--亲测C语言环境搭建并运行一个简单的贪吃蛇(含编译后的exe)_window
    Springboot福佳生活超市进销存管理系统 毕业设计-附源码261620
    input,textarea隐藏边框
    第五话、离职前被老板威胁是一种什么样的体验
    2022年最新宁夏交安安全员考试模拟题库及答案
    windows10安装蓝牙驱动方法步骤
  • 原文地址:https://blog.csdn.net/Long_xu/article/details/127919041