C++11与线程相关的新特性

C++11 与线程相关的新特性

  • std::thread
  • std::mutex
    • std::lock相关
  • std::atomic
  • std::call_once
  • std::condition_variable
  • std::future
  • std::async

std::thread

   在C++11之前我们会用pthread_xxx来创建线程,相对来说是比较繁琐且不易读的。因此C++11引入了std::thread来创建线程,支持对线程join或者detach的操作,代码如下:

#include 
#include 
#include 

#include 

using namespace std;

void fun(int i ){
    std::cout << i << " thread id: " << std::this_thread::get_id() << std::endl;
}

void* fun_p(void *){
    std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
}

int main(int argc, char** argv){

    auto fun_1 = [](int k){
        for(int i = 0; i < k; ++i)
            std::cout << k << " thread id: " << std::this_thread::get_id() << std::endl;
            sleep(1);
    };

    // c++11
    std::thread thread_1(fun, 1);
    std::thread thread_2(fun_1, 2);
    
    //joinable 检测子线程是否可以被join
    //join 等待子线程运行结束后,主线成再结束;
    //detach 是将子线程的管理权限交给系统root去管理,也就是当主线程释放后,子线程可能仍在运行。
    if(thread_1.joinable())
        thread_1.join();
    if(thread_2.joinable())
        thread_2.join();

    // pthread
    pthread_t t1;
    pthread_create(&t1, nullptr, fun_p, nullptr);
    pthread_join(t1, nullptr);
    return 0;
}

  上面实例只是一次简单的线程创建和线程释放的操作,需要手动的创建和释放线程,这样有时候会将释放线程的操作给遗漏掉,这样子线在未执行完成的时候主线程有限结束,会产生一些不可控的程序出错。因此我们将线程的管理进行封装,如下所示:

#include 
#include 
#include 

using namespace std;
class ThreadGuard{
    public:
        enum class DesAction{ join, detach };

        ThreadGuard(std::thread t, DesAction a)
            :t_(std::move(t)), action_(a){

        }

        ~ThreadGuard(){
            if(t_.joinable() && (action_ == DesAction::join)){
            	//get_id 获取子线程的线程号
                std::cout << "wait join thread: " << t_.get_id() << std::endl;
                t_.join();
                std::cout << "joined thread" << std::endl;
            } else {
                t_.detach();
            }
        }

        std::thread& get() { return t_; }

        ThreadGuard(ThreadGuard&&) = default;
        ThreadGuard& operator=(ThreadGuard&&) = default;

    private:
        std::thread t_;
        DesAction action_;
}; //ThreadGuard



int main(int argc, char** argv){

    auto fun_1 = [](int k){
        for(int i = 0; i < k; ++i)
            std::cout << k << " thread id: " << std::this_thread::get_id() << std::endl;
            sleep(1);
    };

    ThreadGuard t(std::thread(fun_1, 3), ThreadGuard::DesAction::join);
    return 0;
}

  thread除了以上的基本功能外,还有额外的一些工功能,例如: 获取cpu个数、线程休眠等,如下所示:

//获取cpu的个数
std::cout << "cpu number: " << t_.hardware_concurrency() << std::endl;
//handle用于thread的相关处理
t_.native_handle()// 线程休眠
std::this_thread::sleep_for(std::chrono::seconds(1));

std::mutex

  std::mutex是一种线程同步的手段,用于保存多线程同时操作的共享资源。mutex分为四种,如下:
std::mutex:独占的互斥量,不能地递归使用,不带超时功能;
std::recurise_mutex:递归互斥量,可重入,不带超时功能;
std::timed_mutex:带超时功能,不能递归;
std::recurise_timed_mutex:带递归、带超时功能。

//std::mutex
std::mutex mutex_;
int k = 3;

int mutex_test(){
    auto fun_1 = [](){
        mutex_.lock();
        for(int i = 0; i < k; ++i){
            std::cout << k << " thread id: " << std::this_thread::get_id() << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        mutex_.unlock();
    };
    std::thread t[5];
    for(int i = 0; i < 5; i++)
        t[i] = std::thread(fun_1);
    std::cout << "*********************" << std::endl;
    for(int i = 0; i < 5; ++i){
        if (t[i].joinable())
            t[i].join();
    }
    std::cout << "Successful" << std::endl;
        
    return 0;
}

//std::timed_mutex
std::timed_mutex timed_mutex_;
int timed_mutex_test(){
    auto fun_1 = [](){
        timed_mutex_.try_lock_for(std::chrono::milliseconds(2000));
        for(int i = 0; i < k; ++i){
            std::cout << k << " thread id: " << std::this_thread::get_id() << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(10));
        }
        timed_mutex_.unlock();
    };
    std::thread t[2];
    for(int i = 0; i < 2; i++)
        t[i] = std::thread(fun_1);

    for(int i = 0; i < 2; ++i){
        if (t[i].joinable())
            t[i].join();
    }
    std::cout << "Successful" << std::endl;
        
    return 0;

}

std::lock相关

  在这里主要介绍两种RAII方式的锁封装,可以动态的释放锁资源,防止线程由于编码失误导致一直持有锁。C++11主要有std::unique_lock和std::lock_guard两种方式,使用方式都比较类似,如下所示:

std::mutex mutex_;
int k = 3;
int lock_test(){
    auto fun_1 = [](){
        std::unique_lock<std::mutex> lock_(mutex_);
        //std::lock_guard lock(mutex_);
        for(int i = 0; i < k; ++i){
            std::cout << k << " thread id: " << std::this_thread::get_id() << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    };
    std::thread t[2];
    for(int i = 0; i < 2; i++)
        t[i] = std::thread(fun_1);

    for(int i = 0; i < 2; ++i){
        if (t[i].joinable())
            t[i].join();
    }
    std::cout << "Successful" << std::endl;
        
    return 0;

}

  std::lock_guard相比于std::unique_lock更加轻量级,少了一些成员函数,std::unique_lock类有unlock函数,可以手动释放锁,所以条件变量都配合std::unique_lock使用,而不是std::lock_guard,因为条件变量在wait时需要手动释放锁的能力,关于std::condition_variable,后面会讲到。

std::atomic

  C++11提供了原子类型的std::atomic,理论上这个T可以是任意类型,但是在平时只存放整形,其余的均每怎么用过,整形有这种源自变量已经足够方便,就不需要使用std::mutex来保护变量了。如下所示:

#include 
#include 

//old counter
class Counter{
    private:
        int count;
        std::mutex mutex_;
    public:

        Counter()
            :count(0){

        }

        void add(){
            std::lock_guard<std::mutex> lock_(mutex_);
            ++count;
        }
        void sub(){
            std::lock_guard<std::mutex> lock_(mutex_);
            --count;
        }

        int get(){
            std::lock_guard<std::mutex> lock_(mutex_);
            return count;

        }
};

//new counter
class NewCounter{

    private:
        std::atomic<int> count;
        std::mutex mutex_;
    public:

        NewCounter()
            :count(0){

        }

        void add(){
            ++count;
        }
        void sub(){
            --count;
        }

        int get(){
            return count;
        }
};

std::call_once

  C++11提供了std::call_once来保证某函数在多线程环境中只调用一次,它需要配合std::once_flag使用,代码如下:

std::once_flag once_flag_;

int call_once_test(){
    auto fun = [](){
        std::call_once(once_flag_, [](){
            std::cout << "Hello World" << ", thread id: " << std::this_thread::get_id() << std::endl;
        });
        std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
    };

    std::thread t[3];
    for(int i = 0; i < 3; ++i){
        t[i] = std::thread(fun);
    }

    for(auto& it : t){
        if (it.joinable())
        it.join();
    }
}

std::condition_variable

  条件变量是C++11引入的一种同步机制,它可以阻塞一个或多个线程,知道有线程通知或者超时时,才会唤醒正在阻塞的线程。条件变量一般配合锁来使用。

class TestConditionVar{

    explicit TestConditionVar(uint32_t count)
        :count_(count){
        }

    void CountDown(){
            std::unique_lock<std::mutex> lock(mutex_);
            count_--;
            if( 0 == count_ ){
                cv_.notify_all();
            }

    }

    void Await(uint32_t time_ms = 0){
        std::unique_lock<std::mutex> lock(mutex_);
        while ( count_ > 0 ){
            if ( time_ms > 0 ){
                cv_.wait_for(lock, std::chrono::milliseconds(time_ms));
            } else {
                cv_.wait(lock);
            }
        }
        std::cout << "Await end..." <<std::endl;
    }

    private:
        std::condition_variable cv_;
        std::mutex mutex_;
        int count_;
};

std::future

  C++11提供了异步操作类,主要有std::future、std::promise和std::package_task,std::future比std::thread高级一些,它作为异步结果的传输通道,通过get()可以很方便的获取线程函数的返回值;std::promise用来包装一个值,将数据和future绑定起来;而std::package_task用来包装一个调用对象,将函数和future绑定起来,方便异步调用。future是不可以复制的,若需要复制到容器中,需要使用std::shared_future。

#include 
#include 
#include 
//std::future std::promise
void fun_fut(std::future<std::string>& fu_i){
    std::string x = fu_i.get();
    std::cout << "value: " << x << std::endl;
}
void future_test(){
    std::promise<std::string> pro;
    std::future<std::string>  fut = pro.get_future();
    std::thread t(fun_fut, std::ref(fut));
    pro.set_value("Hello World");
    if (t.joinable())
        t.join();
}

//std::future std::packaged_task
std::string fun_fut_2(std::string fu_i){
    return fu_i + ", Love you";
}
void future_test_2(){
    std::packaged_task<std::string(std::string)> task(fun_fut_2);
    std::future<std::string>  fut = task.get_future();
    std::thread(std::move(task), "Hello World").detach();

    std::cout << "value: " << fut.get() << std::endl;
}

  std::future用于访问异步操作的结果,而std::packaged_task和std::promise在future高一层,它们内部均有一个future,promise包装的是一个值,package_task包装的一个函数,当需要获取线程中的某个值,可以使用promise,当需要获取线程函数返回值,可以使用package_task。

std::async

  async是比future、package_task、promise更高级的操作,它是基于任务的异步操作,通过async可以直接创建异步的任务,返回的结果会保存在future中,不需要package_task和promise那么复杂,关于线程操作应该优先使用async。

#include 
#include 
#include 
int asnyc_fun(int x, int y){
    sleep(10);
    return x * y;
}

int asnyc_test(){
    auto fun = [](int x, int y){ return x * y; };
    auto res_ = std::async(fun, 5, 2);
    std::cout << res_.get() << std::endl;
    auto res = std::async(asnyc_fun, 5, 3);
    std::cout << "waiting ..." << std::endl;
    //当asnyc_fun函数执行结束后,才会执行下面语句
    std::cout << res.get() << std::endl;

    return 0;
}

你可能感兴趣的:(C/C++,嵌入式)