c++11多线程操作

  • 线程

    • thread
    int main()
    {
    thread t1(Test1);
    t1.join();
    thread t2(Test2);
    t2.join();
    thread t3 = t1;
    thread t4(t1);
    thread t5 = std::move(t1);
    thread t6(std::move(t1));
    return 0;
    }

    t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是:

    thread(const thread&) = delete;
    thread& operator=(const thread&) = delete;

    被禁用了,但是t5, t6线程是创建成功的。std::move把t1转换为右值,调用的是函数原型为thread& operator=(thread&& _Other) noexceptthread(thread&& _Other) noexcept

    当线程对象t1被移动拷贝和移动赋值给t5和t6的时候,t1就失去了线程控制权,也就是一个线程只能同时被一个线程对象所控制。最直观的是t1.joinable()返回值为false,joinable()函数后面介绍。

    使用类成员函数作为线程参数

    class Task
    {
    public:
    Task(){}
    void Task1() {}
    void Task2() {}
    private:
    }; int main()
    {
    Task task;
    thread t3(&Task::Task1, &task);
    t3.join();
    return 0;
    }

    关键点是要创建一个类对象,并作为第二个参数传入thread()线程的构造函数中去。

  • 管理当前线程的函数

    • yield

    此函数的准确性为依赖于实现,特别是使用中的 OS 调度器机制和系统状态。例如,先进先出实时调度器( Linux 的 SCHED_FIFO )将悬挂当前线程并将它放到准备运行的同优先级线程的队列尾(而若无其他线程在同优先级,则 yield 无效果)。

    #include <iostream>
    #include <chrono>
    #include <thread> // 建议其他线程运行一小段时间的“忙睡眠”
    void little_sleep(std::chrono::microseconds us)
    {
    auto start = std::chrono::high_resolution_clock::now();
    auto end = start + us;
    do {
    std::this_thread::yield();
    } while (std::chrono::high_resolution_clock::now() < end);
    } int main()
    {
    auto start = std::chrono::high_resolution_clock::now(); little_sleep(std::chrono::microseconds(100)); auto elapsed = std::chrono::high_resolution_clock::now() - start;
    std::cout << "waited for "
    << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
    << " microseconds\n";
    }
    • get_id

    这个函数不用过多介绍了,就是用来获取当前线程id的,用来标识线程的身份。

     std::thread::id this_id = std::this_thread::get_id();
    • sleep_for

    位于this_thread命名空间下,msvc下支持两种时间参数。

    std::this_thread::sleep_for(2s);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    • sleep_untile

    参数构建起来挺麻烦的,一般场景下要求线程睡眠的就用sleep_for就行了

    using std::chrono::system_clock;
    time_t tt = system_clock::to_time_t(system_clock::now());
    struct std::tm *ptm = localtime(&tt);
    std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
  • 互斥

    • mutex

    对于互斥量看到一个很好的比喻:

    单位上有一台打印机(共享数据a),你要用打印机(线程1要操作数据a),同事老王也要用打印机(线程2也要操作数据a),但是打印机同一时间只能给一个人用,此时,规定不管是谁,在用打印机之前都要向领导申请许可证(lock),用完后再向领导归还许可证(unlock),许可证总共只有一个,没有许可证的人就等着在用打印机的同事用完后才能申请许可证(阻塞,线程1lock互斥量后其他线程就无法lock,只能等线程1unlock后,其他线程才能lock),那么,这个许可证就是互斥量。互斥量保证了使用打印机这一过程不被打断。

    代码示例:

    mutex mtx;
    
    int gNum = 0;
    void Test1()
    {
    mtx.lock();
    for(int n = 0; n < 5; ++n)
    gNum++;
    mtx.unlock();
    } void Test2()
    {
    std::cout << "gNum = " << gNum << std::endl;
    } int main()
    {
    thread t1(Test1);
    t1.join();
    thread t2(Test2);
    t2.join();
    return 0;
    }

    join()表示主线程等待子线程结束再继续执行,如果我们的期望是打印循环自增之后的gNum的值,那t1.join()就放在t2创建之前调用。因为t2的创建就标志着t2线程创建好然后开始执行了

    通常mutex不单独使用,因为lock和unlock必须配套使用,如果忘记unlock很可能造成死锁,即使unlock写了,但是如果在执行之前程序捕获到异常,也还是一样会死锁。如何解决使用mutex造成的死锁问题呢?下面介绍unique_gard和lock_guard的时候详细说明。

    • timed_mutex

    提供互斥设施,实现有时限锁定

    • recursive_mutex

    提供能被同一线程递归锁定的互斥设施

    • recursive_timed_mutex

    提供能被同一线程递归锁定的互斥设施,并实现有时限锁定

  • 通用互斥管理

    • lock_guard
    void Test1()
    {
    std::lock_guard<std::mutex> lg(mtx);
    for(int n = 0; n < 5; ++n)
    {
    gNum++;
    std::cout << "gNum = " << gNum << std::endl;
    }
    }
    int main()
    {
    thread t1(Test1);
    thread t2(Test1);
    t1.join();
    t2.join();
    return 0;
    }

    lock_guard相当于利用RAII机制(“资源获取就是初始化”)把mutex封装了一下,在构造中lock,在析构中unlock。避免了中间过程出现异常导致的mutex不能够正常unlock.

    • scoped_lock(c++17)
    • unique_lock
    • defer_lock_t
    • try_to_lock_t
    • adopt_lock_t
    • defer_lock
    • try_to_lock
    • adopt_lock
  • 通用锁算法

    • try_lock
    • lock
  • 单次调用

    • once_flag
    • call_once
  • 条件变量

    • condition_variable
    • condition_variable_any
    • notify_all_at_thread_exit
    • cv_status
  • Future

    • promise
    • packaged_task
    • future
    • shared_future
    • async
    • launch
    • future_status
    • Future错误
      • future_error
      • future_category
      • future_errc

最新文章

  1. c#-冒泡排序-算法
  2. POJ 1182 食物链(带权并查集)
  3. 十大关系数据库SQL注入工具一览
  4. python笔记 - day7-1 之面向对象编程
  5. JSP的设计模式
  6. MVC校验
  7. 硝烟中的Scrum和XP-我们如何实施Scrum 15)多团队 Part 2/2 16)地理分散 17)检查列表 18)其他
  8. delphi调用java编写的webservice
  9. UI控件自定义tableView的分割线的样式
  10. jaspersoft 5.6.0 相关问题
  11. JS获取标签方法及兼容处理
  12. JS 数组及函数
  13. (转)spring aop(下)
  14. Windows Server2012R2 添加Microsoft .NET Framework 3.5 功能失败的解决方法
  15. Django-rest-framework 接口实现 分页:(Pagination) 解析器(Parser) 渲染器(renderer)
  16. IdentityServer4【Introduction】之支持的规范
  17. 20175211 2018-2019-2 《Java程序设计》第二周学习总结
  18. MySQL:数据库入门篇3
  19. 使用 gulp 压缩图片
  20. angular的$watch,$digest和$apply

热门文章

  1. python numpy数组操作
  2. latex:公式中的文字
  3. Java多线程_生产者消费者模式1
  4. Go语言从入门到高薪之路(一)-- 初识与安装
  5. [NOIP2018]保卫王国 题解
  6. seo快速排名利器之高权重二级域名
  7. 关于windou环境下使用http或者ftp搭建网络hu共享
  8. 利用GetPrivateProfileString读取配置文件(.ini)
  9. Easy Problem(等差数列求和导公式)
  10. 知识点干货——CSS动画