C/C++C++11初探多线程

Posted mick_seu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++C++11初探多线程相关的知识,希望对你有一定的参考价值。

首先推荐一个博客:

C++11 并发指南系列(256code)

本系列很多参考该专栏


之前在Linux下,一直使用 Pthread 使用多线程变成。C++11 新标准中引入了五个头文件来支持多线程编程,他们分别是<atomic> , <thread>, <mutex>, <condition_variable> 和 <future>。这样,你就可以在语言层面编写多线程程序了,直接的好处就是代码的可移植性得到了提高。


<atomic>:该头文主要声明了两个类,std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
<thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
<mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard,std::unique_lock以及其他的类型和函数。
<condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
<future>:该头文件主要声明了 std::promise,std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。


本片主要介绍 std::thread类的使用。


std::thread类的声明:

namespace std 
    class thread 
        public:
            // 类型声明:
            class id;
            typedef implementation-defined native_handle_type;

            // 构造函数、拷贝构造函数和析构函数声明:
            thread() noexcept;
            template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
            ~thread();
            thread(const thread&) = delete;
            thread(thread&&) noexcept;
            thread& operator=(const thread&) = delete;
            thread& operator=(thread&&) noexcept;

            // 成员函数声明:
            void swap(thread&) noexcept;
            bool joinable() const noexcept;
            void join();
            void detach();
            id get_id() const noexcept;
            native_handle_type native_handle();
            
            // 静态成员函数声明:
            static unsigned hardware_concurrency() noexcept;
    ;


std::this_thread命名空间里的相关函数:

namespace std 
    #define __STDCPP_THREADS__ __cplusplus
    class thread;
    void swap(thread& x, thread& y);
    namespace this_thread 
        thread::id get_id();
        void yield();

        template <class Clock, class Duration>
        void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);

        template <class Rep, class Period>
        void sleep_for(const chrono::duration<Rep, Period>& rel_time);
            

一个例子:

#include <iostream>
#include <thread>
#include <chrono>

void f1() 
    for (int i = 0; i < 5; ++i) 
        std::cout << "Thread " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    


void f2(int& n) 
    for (int i = 0; i < 5; ++i) 
        ++n;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    


int main() 
	int n = 0;
    std::thread t1; 					// t1 is not a thread
	std::cout << t1.get_id() << std::endl;
	std::cout << "t1 : " << std::boolalpha << t1.joinable() << std::endl;
    std::thread t2(f1); 				// pass by value
    std::thread t3(f2, std::ref(n)); 	// pass by reference
	std::cout << "t2 : " << std::boolalpha << t2.joinable() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(10));
    t1 = std::move(t2); 				// t1 is now running f1(). t2 is no longer a thread
	std::cout << t1.get_id() << std::endl;
	std::cout << "t1 : " << std::boolalpha << t1.joinable() << std::endl;
	std::cout << "t2 : " << std::boolalpha << t2.joinable() << std::endl;
    t1.join();
    t3.join();
    std::cout << "Final value of n is " << n << '\\n';

输出如下:

thread::id of a non-executing thread
t1 : false
Thread 140649719494400
t2 : true
Thread 140649719494400
Thread 140649719494400
Thread 140649719494400
Thread 140649719494400
140649719494400
t1 : true
t2 : false
Final value of n is 5

joinable() 可以检查线程是否可被 join。由默认构造函数创建的线程是不能被 join 的。线程被创建后在尚未join()之前,都是可以被 join 的。

成员函数get_id()和std::this_thread::get_id()用于获取线程ID,返回一个std::thread::id对象。

成员函数 swap() 和 非成员swap()可用于交换两个线程对象所代表的底层句柄。

#include <iostream>
#include <thread>
#include <chrono>

void f1() 
    for (int i = 0; i < 5; ++i) 
        //std::cout << "Thread " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    


void f2(int& n) 
    for (int i = 0; i < 5; ++i) 
        ++n;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    


int main() 
    std::thread t1(f1); 				
    std::thread t2(f1); 				
	std::cout << "t1: " << t1.get_id() << std::endl;
	std::cout << "t2: " << t2.get_id() << std::endl;

	t1.swap(t2);
	std::cout << "t1: " << t1.get_id() << std::endl;
	std::cout << "t2: " << t2.get_id() << std::endl;

	std::swap(t1, t2);
	std::cout << "t1: " << t1.get_id() << std::endl;
	std::cout << "t2: " << t2.get_id() << std::endl;

    t1.join();
    t2.join();

输出如下:

t1: 140219993110272
t2: 140219984717568
t1: 140219984717568
t2: 140219993110272
t1: 140219993110272
t2: 140219984717568

成员函数 detach(): 将子线程与父线程分离,使子线程执单独执行。线程资源回收由系统完成,父线程不用再调用 join(),确切的说,不能调用join(),会造成异常,类似的 join() 也不能调用两次。

#include <iostream>
#include <thread>
#include <chrono>

void f1() 
    for (int i = 0; i < 5; ++i) 
        std::cout << "Thread " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    


int main() 
    std::thread t1(f1);
	std::cout << "t1: " << std::boolalpha << t1.joinable() << std::endl;
	t1.detach();
    std::this_thread::sleep_for(std::chrono::seconds(1));
	std::cout << "t1: " << std::boolalpha << t1.joinable() << std::endl;
输出:

t1: true
Thread 140507549996800
t1: false

native_handle() :返回原生句柄(std::thread 的实现和操作系统相关,因此该函数返回与 std::thread 具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 pthread 库)


hardware_concurrency() [static]: 检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)。

  #include <iostream>
  #include <thread>
   
  int main() 
      unsigned int n = std::thread::hardware_concurrency();
      std::cout << n << " concurrent threads are supported.\\n";
  

sleep_until(): 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。

sleep_for(): 线程休眠某个指定的时间片(time span),该线程才被重新唤醒。

yield(): 当前线程放弃执行,操作系统调度另一线程继续执行。

#include <iostream>
#include <thread>
#include <chrono>

void LittleRest(std::chrono::microseconds sec) 
	auto start = std::chrono::high_resolution_clock::now();
	auto end = start + sec;
	uint32_t count = 0;
	do 
		count++;
		std::this_thread::yield();
     while (std::chrono::high_resolution_clock::now() < end);
	std::cout << count << std::endl;


int main() 
	LittleRest(std::chrono::microseconds(1000));
输出:1478


以上是关于C/C++C++11初探多线程的主要内容,如果未能解决你的问题,请参考以下文章

C/C++C++11新特性:初探右值引用与转移语义

C++11多线程与并发初探

面试必问的C++11新特性代码实践

c_cpp C ++ 11标准新特性:Defaulted和Deleted函数

第三章--Win32程序的执行单元(部分概念及代码讲解)(上 -- 多线程)

C/C++ 中的多线程状态机实现