美文网首页
C11线程并发

C11线程并发

作者: 一石头快 | 来源:发表于2018-11-05 09:54 被阅读159次

    C++11的线程并发要点

    1.线程构建

    a.通过 std::thread(funPoint,param1,param2) 来创建.当构造函数的参数为空时.默认thread是joinable = false的.

    2.线程的行为

    a.join
    b.attach

    3.线程参数的注意事项

    ==a==.注意如下语句:
    void oops(int some_param)
    {
    char buffer[1024]; // 1
    sprintf(buffer, "%i",some_param);
    std::thread t(f,3,buffer); // 2
    t.detach();
    }
    
    void not_oops(int some_param)
    {
    char buffer[1024];
    sprintf(buffer,"%i",some_param);
    std::thread t(f,3,std::string(buffer)); // 使用std::string,避免悬垂指针
    t.detach();
    }
    
    

    原因: buffer②是一个指针变量,指向本地变量,然后本地变量通过buffer传递到新线
    程中②。并且,函数有很大的可能,会在字面值转化成 std::string 对象之前崩溃(oops),从
    而导致线程的一些未定义行为。解决方案就是在传递到 std::thread 构造函数之前就将字面值
    转化为 std::string 对象。

    ==b==. std::unique_ptr 和std::move
    void process_big_object(std::unique_ptr<big_object>);
    std::unique_ptr<big_object> p(new big_object);
    p->prepare_data(42);
    std::thread t(process_big_object,std::move(p));
    

    "移动"是指:原始对象中的数据转移给另一对象,而转移的这些数据就不再在原始对象中保存了. std::unique_ptr 就是这样一种类型(译者:C++11中的智能指针),这种类型
    为动态分配的对象提供内存自动管理机制(译者:类似垃圾回收)。同一时间内,只允许一个 std::unique_ptr 实现指向一个给定对象,并且当这个实现销毁时,指向的对象也将被删除。

    ==c==. std::thread::id 或者 std::this_thread::get_id()可以用来区分是否主线程.

    4.线程锁

    ==a==.我们要引入互斥量mutex和为了方便使用互斥量的std::lock_guard.当我们要保护一个变量免予同时修改的时候.我们使用:std::lock_gard<std::mutex> guard(some_mutex) 来进行.
    ==b==.层次锁:先获取高级别的锁.再获取低级别的锁---符合要求.
    ==c==.std::unique_lock 和std:unique_guard一样.区别在于更灵活.使用都是:std::unique_lock<std::mutxt> m_lok(m_mutex); 灵活性体现在.比如可以转移所有权.如例子:

    std::unique_lock<std::mutex> get_lock()
    {
    extern std::mutex some_mutex;
    std::unique_lock<std::mutex> lk(some_mutex);
    prepare_data();
    return lk; // 1
    }
    void process_data()
    {
    std::unique_lock<std::mutex> lk(get_lock()); // 2 这里就是用1的unique_lock
    do_something();
    }
    

    ==d==.双重锁.不建议使用.所以C11增加了一个.std::once_flag 和std::call_once

    5.线程同步

    ==a==. std:condition_variable 这个可以用来阻塞线程.采用wait表示阻塞。采用notify_all表示唤醒。如下代码

    std::condition_variable cv; // 全局条件变量.
    bool ready = false; // 全局标志位.
    
    void do_print_id(int id)
    {
        std::unique_lock <std::mutex> lck(mtx);
        while (!ready) // 如果标志位不为 true, 则等待...
            cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,
        // 线程被唤醒, 继续往下执行打印线程编号id.
        std::cout << "thread " << id << '\n';
    }
    
    void go()
    {
        std::unique_lock <std::mutex> lck(mtx);
        ready = true; // 设置全局标志位为 true.
        cv.notify_all(); // 唤醒所有线程.
    }
    

    ==b==.std::future 从异步对象中返回值。一个简单的例子

     std::future<bool> fut = std::async(is_prime, 700020007); //future一般是一个异步对象
      std::cout << "please wait";
      std::chrono::milliseconds span(100);  //设置一个超时时间
      while (fut.wait_for(span) != std::future_status::ready)  //等待超时时间.超时时间内如果有给结果.那么就有一个std::future_status::read
        std::cout << ".";
      std::cout << std::endl;
    
      bool ret = fut.get(); //获取超时对象给予的值
    

    ==c==:std::packaged_task 异步对象以及处理。因线程本身不返回值。我们可以把函数打包到一个线程中。再用std::future来获取得到。

    include <iostream>
    #include <future>
    #include <chrono>
    #include <functional>
     
    int Test_Fun(int a, int b, int &c)
    {
        //a=1,b=2,c=0
     
        //突出效果,休眠5s
        std::this_thread::sleep_for(std::chrono::seconds(5));
     
        //c=233
        c = a + b + 230;
     
        return c;
    }
     
    int main()
    {
        //声明一个std::packaged_task对象pt1,包装函数Test_Fun
        std::packaged_task<int(int, int, int&)> pt1(Test_Fun);
        //声明一个std::future对象fu1,包装Test_Fun的返回结果类型,并与pt1关联
        std::future<int> fu1 = pt1.get_future();
     
        //声明一个变量
        int c = 0;
     
        //创建一个线程t1,将pt1及对应的参数放到线程里面执行
        std::thread t1(std::move(pt1), 1, 2, std::ref(c));
        
        //阻塞至线程t1结束(函数Test_Fun返回结果)
        int iResult = fu1.get();
     
        std::cout << "执行结果:" << iResult << std::endl;   //执行结果:233
        std::cout << "c:" << c << std::endl;    //c:233
     
        return 1;
    }
    

    ==d==: std::promise 提供了一种方式。用于不同的线程间可以共享数据。这个数据可以进行计算。入门例子如下

    
    #include <iostream>
    #include <future>
    #include <chrono>
     
    void Thread_Fun1(std::promise<int> &p)
    {
        //为了突出效果,可以使线程休眠5s
        std::this_thread::sleep_for(std::chrono::seconds(5));
     
        int iVal = 233;
        std::cout << "传入数据(int):" << iVal << std::endl;
     
        //传入数据iVal
        p.set_value(iVal);
    }
     
    void Thread_Fun2(std::future<int> &f)
    {
        //阻塞函数,直到收到相关联的std::promise对象传入的数据
        auto iVal = f.get();        //iVal = 233
     
        std::cout << "收到数据(int):" << iVal << std::endl;
    }
     
    int main()
    {
        //声明一个std::promise对象pr1,其保存的值类型为int
        std::promise<int> pr1;
        //声明一个std::future对象fu1,并通过std::promise的get_future()函数与pr1绑定
        std::future<int> fu1 = pr1.get_future();
     
        //创建一个线程t1,将函数Thread_Fun1及对象pr1放在线程里面执行
        std::thread t1(Thread_Fun1, std::ref(pr1));
        //创建一个线程t2,将函数Thread_Fun2及对象fu1放在线程里面执行
        std::thread t2(Thread_Fun2, std::ref(fu1));
     
        //阻塞至线程结束
        t1.join();
        t2.join();
     
        return 1;
    

    因为future后的get函数.获取后其他线程就获取不到了.没办法让多个线程等待同一个事件.这时候就需要std::shared_future了.简单的例子如下:

    #include <iostream>       // std::cout
    #include <future>         // std::async, std::future, std::shared_future
    
    int do_get_value() { return 10; }
    
    int main ()
    {
        std::future<int> fut = std::async(do_get_value);
        std::shared_future<int> shared_fut = fut.share();
    
        // 共享的 future 对象可以被多次访问.
        std::cout << "value: " << shared_fut.get() << '\n';
        std::cout << "its double: " << shared_fut.get()*2 << '\n';
    
        return 0;
    }
    

    时间周期

    ==a==.
    std::chrono::system_clock::now() 是将返回系统时钟的当前时间.chrono::duration_cast则可以进行时间周期的转换。如下例子:

    std::chrono::minutes t1( 10 );
    std::chrono::seconds t2( 60 );
    std::chrono::seconds t3 = t1 - t2;
    std::cout << t3.count() << " second" << std::endl;
    //接下来把秒转化为分
    out << chrono::duration_cast<chrono::minutes>( t3 ).count() <<” minutes”<< endl;
    //将会输出:  9 minutes
    

    std::chrono::time_point 用于解决时间戳的问题,比如我们计算耗时:

    
    #include <iostream>
    #include <iomanip>
    #include <ctime>
    #include <chrono>
    
    int main()
    {
       std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
       std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
       std::cout << "24 hours ago, the time was "
                 << std::put_time(std::localtime(&now_c), "%F %T") << '\n';
    
       std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
       std::cout << "Hello World\n";
       std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
       std::cout << "Printing took "
                 << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
                 << "us.\n";
    
    

    ==c==.
    RxCpp 响应式编程.
    ==d==. 原子操作
    std::atomic_flag 原子对象没有拷贝和赋值构造函数.

    相关文章

      网友评论

          本文标题:C11线程并发

          本文链接:https://www.haomeiwen.com/subject/azvjxqtx.html