首页 > 编程语言 >C++并发编程 [02] :线程管控

C++并发编程 [02] :线程管控

时间:2022-10-04 15:12:56浏览次数:44  
标签:02 std thread do C++ guard 线程 my

发起线程

线程通过构建 std::thread 对象而启动,该对象指明线程要运行的任务。可以传入任何可调类型std::thread 来构建一个 std::thread 对象。 需要包含头文件 <thread>

  • 传入的可调类型可以是函数:
void do_some_work();
std::thread my_thread(do_some_work);
  • 传入的可调类型可以是带有函数调用操作符的类:
class background_task {
public:
    void operator()() const {
        do_something();
        do_something_else();
    }
};
background_task f;
std::thread my_thread(f);

此时要防范二义性,对于有可能被解释成函数声明的C++语句,编译器就肯定会将其解释为函数声明。如 std::thread my_thread(background_task()); 语句本意是传入一个临时的匿名函数对象发起新线程,但却会被解释成一个函数声明。可以使用统一初始化语法来解决:

std::thread my_thread((background_task()));
std::thread my_thread{background_task()};
  • 传入的可调类型可以是lambda表达式
std::thread my_thread([]{
    do_something();
    do_something_else();
});

必须时刻注意在线程运行结束前,要保证它所访问的外部数据必须始终正确、有效。尤其要注意传给线程的可调对象含有指针或引用时,引用的外部对象在线程运行期间是否可能会被销毁,是否可能出现悬空指针。

等待线程完成

std::thread my_thread(foo);
my_thread.join(); //等待线程结束

对于某个给定的线程,join() 只能调用一次,只要 std::thread 对象曾经调用过 join(),线程就不再可汇合,成员函数 joinable() 将返回 false

利用 RAII 过程等待线程完成

如果打算等待线程结束,但在调用 join() 前就发生了异常,这会导致 join() 调用会被略过。为了在可能出现异常的情况下等待线程完成,最好是利用RAII过程。

RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。

class thread_guard {
    std::thread& t;
public:
    explicit thread_guard(std::thread& t_) :t(t_) {}
    ~thread_guard() {
        if (t.joinable()) t.join(); // join() 只能被调用一次
    }
    thread_guard(thread_guard const&) = delete; // 禁止拷贝构造函数
    thread_guard& operator=(thread_guard const&) = delete; // 禁止赋值构造函数
};

struct func {
    int& i;
    func(int& i_) :i(i_) {}
    void operator()() {
        for (unsigned j = 0; j < 1000000; ++j)
            do_something(i); // i是引用,需要注意可能导致悬空引用
    }
};

void f() {
    int some_local_state = 0;
    func my_func(some_local_state);
    std::thread t(my_func);
    thread_guard(t);
    do_something_in_current_thread();
}

当主线程执行到 f() 末尾时,按构建的逆序,所有局部对象都会被销毁,thread_guard 的对象 g 首先被销毁,在其析构函数中调用新线程的 join()。即使 do_something_in_current_thread() 发生异常,g 的析构函数仍会被调用,以上行为仍会发生。

分离线程

std::thread my_thread(foo);
my_thread.detach(); //分离线程

只有 joinable() 返回 true,线程才可被分离。分离后 joinable() 将返回 false

向线程函数传递参数

直接向 std::thread 的构造函数追加更多参数即可:

#include <iostream>
#include <thread>

void add(int a, int b) {
    std::cout << a + b << std::endl;
}

int main() {
    std::thread t(add, 2, 3); // 输出5
    t.join();

    return 0;
}

未完待续

标签:02,std,thread,do,C++,guard,线程,my
From: https://www.cnblogs.com/AEMShana/p/16753770.html

相关文章