多线程与并发:multithreading and Concurrency

本文探讨了C++中的多线程编程,重点讲解了call_once保证初始化函数仅执行一次,线程局部存储(thread_local)避免数据竞争,以及原子变量(atomic)实现线程安全的读写。此外,还提到了async()函数用于异步任务执行,以简化线程管理。
摘要由CSDN通过智能技术生成

线程(thread):

在 C++ 语言里,线程就是一个能够独立运行的函数。比如你写一个 lambda 表达式,就可以让它在线程里跑起来:


auto f = []()                // 定义一个lambda表达式
{
    cout << "tid=" <<
        this_thread::get_id() << endl;
};

thread t(f);                // 启动一个线程,运行函数f

任何程序一开始就有一个主线程,它从 main() 开始运行。主线程可以调用接口函数,创建出子线程。子线程会立即脱离主线程的控制流程,单独运行,但共享主线程的数据。程序创建出多个子线程,执行多个不同的函数,也就成了多线程。多线程的好处,比如任务并行、避免 I/O 阻塞、充分利用 CPU、提高用户界面响应速度,等等。坏处,比如同步、死锁、数据竞争、系统调度开销等。

所以在 C++ 多线程编程里读取 const 变量总是安全的,对类调用 const 成员函数、对容器调用只读算法也总是线程安全的。“读而不写”就不会有数据竞争。

在 C++ 里,有四个基本的工具:仅调用一次、线程局部存储、原子变量和线程对象。

多线程开发实践:

仅调用一次

程序免不了要初始化数据,这在多线程里却是一个不大不小的麻烦。因为线程并发,如果没有某种同步手段来控制,会导致初始化函数多次运行。为此,C++ 提供了“仅调用一次”的功能,可以很轻松地解决这个问题。这个功能用起来很简单,你要先声明一个 once_flag 类型的变量,最好是静态、全局的(线程可见),作为初始化的标志:

static std::once_flag flag;        // 全局的初始化标志

然后调用专门的 call_once() 函数,以函数式编程的方式,传递这个标志和初始化函数。这样 C++ 就会保证,即使多个线程重入 call_once(),也只能有一个线程会成功运行初始化。下面是一个简单的示例,使用了 lambda 表达式来模拟实际的线程函数。你可以把GitHub 仓库里的代码下到本地,实际编译运行看看效果:

https://github.com/chronolaw/cpp_study/blob/master/section3/thread.cpp


auto f = []()                // 在线程里运行的lambda表达式
{   
    std::call_once(flag,      // 仅一次调用,注意要传flag
        [](){                // 匿名lambda,初始化函数,只会执
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值