【cpp】多线程管理:线程

rece condition

  • 条件竞争

一般用vector这样的动态容器存储thread

  • stack ,vector是一个动态容器, 当你拷贝一个vector,标准库会从堆上分配很多内存来完成这次拷贝

线程id是可以比较的

  • 可以作为std hash的键值
  • 可以直接比较
  • 通过std thread get_id 得到

线程所有权可移动,不可拷贝

  • C++标准库中有很多资源占有(resource- owning)类型,比如 std::ifstream , std::unique_ptr 还有 std::thread 都是可移动,但不可 拷贝。
  • 这就说明执行线程的所有权可以在 std::thread 实例中移动
  • 【cpp】多线程管理:线程_第1张图片

线程异常时,仍旧要先调用join

  • 线程在异常时,会触发析构,析构时,会先调用std terminate,
    因此发生异常,还是需要处理join或者detach掉这个线程,然后再让异常触发析构,否则terminate了,还没有join和detach是有问题的。
  • 这是因为如果线程未退出,但是函数已经退出,线程可能还持有函数的局部变量。
  • 单线程代码中,对象销毁之后再去访问,也会产生未定义行为
  • 当在线程运行之后产生异常,在join()调用之前抛出,就意味着 这次调用会被跳过。
  • 避免应用被抛出的异常所终止,就需要作出一个决定。
  • 通常,当倾向于在无异常的情况下使 用join()时,需要在异常处理过程中调用join(),从而避免生命周期的问题。下面的程序清单 是一个例子。

明确要等待线程结束

-【cpp】多线程管理:线程_第2张图片

  • 启动了线程,你需要明确是要等待线程结束(加入式——参见2.1.2节),还是让其自主运行 (分离式——参见2.1.3节)。
  • 如果 std::thread 对象销毁之前还没有做出决定,程序就会终止 ( std::thread 的析构函数会调用 std::terminate() )。
  • 因此,即便是有异常存在,也需要确保 线程能够正确的加入(joined)或分离(detached)。
  • 2.1.3节中,会介绍对应的方法来处理这两 种情况。需要注意的是,必须在 std::thread 对象销毁之前做出决定,否则你的程序将会终 止( std::thread 的析构函数会调用 std::terminate() ,这时再去决定会触发相应异常)。
  • 如果不等待线程,就必须保证线程结束之前,可访问的数据得有效性。这不是一个新问题 ——单线程代码中,对象销毁之后再去访问,也会产生未定义行为——不过,线程的生命周 期增加了这个问题发生的几率。
    这种情况很可能发生在线程还没结束,函数已经退出的时候,这时线程函数还持有函数局部 变量的指针或引用。

线程创建的语法陷阱

  • 如果传递的是 一个临时变量,而不是命名的变量。
  • 编译器认为这个时函数声明,而不是类型对象的定义。
  • 有件事需要注意,当把函数对象传入到线程构造函数中时,需要避免“最令人头痛的语法解 析”(C++’s most vexing parse, 中文简介)。如果你传递了一个临时变量,而不是一个命名的 变量;C++编译器会将其解析为函数声明,而不是类型对象的定义。
  • 这里相当与声明了一个名为my_thread的函数,这个函数带有一个参数(函数指针指向没有参 数并返回background_task对象的函数),返回一个 std::thread 对象的函数,而非启动了一 个线程。【cpp】多线程管理:线程_第3张图片
  • 以下语法可以避免:
    【cpp】多线程管理:线程_第4张图片
  • lambda也可以:
std::thread my_thread([]{ do_something(); do_something_else();
});

向线程传递参数

向 std::thread 构造函数中的可调用对象,或函数传递一个参数时,默认参数要拷贝到线程独立内存中

参数是引用

  • 即使参数是引用的形式,也可以在新线程中进 行访问。
  • 【cpp】多线程管理:线程_第5张图片
  • 上面的第三个参数(char const * )向所需的std string 引用转换,作为f的第二个参数。

函数参数第三个是非常量引用

  • void update_data_for_widget(widget_id w,widget_data& data); //
  • 这样会报错:
std::thread t(update_data_for_widget,w,data); // 2
  • 因为data会被以右值的形式拷贝
  • std ref 则将参数转为引用

【cpp】多线程管理:线程_第6张图片

传递成员函数指针

  • 如果你熟悉 std::bind ,就应该不会对以上述传参的形式感到奇怪,因为 std::thread 构造 函数和 std::bind 的操作都在标准库中定义好了,可以传递一个成员函数指针作为线程函 数,并提供一个合适的对象指针作为第一个参数

【cpp】多线程管理:线程_第7张图片


std thread 对象的不可复制性和可移动性

  • c++标准线程库中和 std::unique_ptr 在所属权上有相似语义类型的类有好几
    种, std::thread 为其中之一。虽然, std::thread 实例不像 std::unique_ptr 那样能占有一 个动态对象的所有权,但是它能占有其他资源:每个实例都负责管理一个执行线程。执行线 程的所有权可以在多个 std::thread 实例中互相转移,这是依赖于 std::thread 实例的可移动 且不可复制性。不可复制保性证了在同一时间点,一个 std::thread 实例只能关联一个执行 线程;可移动性使得开发者可以自己决定,哪个实例拥有实际执行线程的所有权
    【cpp】多线程管理:线程_第8张图片

你可能感兴趣的:(c/c++)