C++11: 多线程thread, 锁lock、lock_guard, 条件变量conditional详解

本文详细介绍了C++11中的多线程概念,包括线程的创建与管理(join、detach)、mutex锁(lock、try_lock、lock_guard、lock_unique)以及条件变量(condition)的使用。通过实例展示了如何在并发环境中确保线程安全和同步。

线程定义

实现并行,避免主线程的阻塞

线程实现
void func()
{
   
   
    cout<<"thread func :"<<this_thread::get_id()<<endl;
}

int main()
{
   
   
    cout<<"main thread :"<<this_thread::get_id()<<endl;
    thread t(func);
    t.join();
    cout<<"thread is over"<<endl;
    return 0;
}
输出:
main thread :1
thread func :2
ref:修改线程函数变量作用域
void func(int &n)
{
   
   
    cout<<n<<endl;
    n = 10;
}

int main()
{
   
   
    int n = 5;
    thread t(func,ref(n));
    t.join();
    cout<<n<<endl; // 10
    cout<<"thread is over"<<endl;
    return 0;
}
输出:
5
10
thread is over
join

join:阻塞主线程,子线程调用jion后,主线程需要等待子线程执行完后,再继续执行
join阻塞主进程结束,对子线程的执行没有影响

code:

void func()
{
   
   
    cout<<"thread func :"<<this_thread::get_id()<<endl;
    this_thread::sleep_for(chrono::seconds(10));
}

void func2()
{
   
   
    cout<<"thread func2 :"<<this_thread::get_id()<<endl;
    this_thread::sleep_for(chrono::
<think>我们需要分析两段代码中的作用域,即的持有时间范围。 第一段代码: { std::lock_guard<std::mutex> lock(mtx); process(); } 第二段代码: std::lock_guard<std::mutex> lock(mtx); { process(); } 解释: std::lock_guard<std::mutex> 是一个RAII对象,它在构造函数中定互斥量,在析构函数中解互斥量。 因此,的作用域(即的生命周期)是由 lock_guard 对象所在的块作用域(即大括号包围的范围)决定的。 第一段代码中,lock_guard 对象在一个显式的块作用域内创建。因此,在进入这个块时被获取,在离开这个块时(即右大括号处)被释放。 所以,保护了 process() 函数调用的整个过程。 第二段代码中,lock_guard 对象创建在外部作用域(比如函数体作用域,或者没有显式大括号的某个作用域),而随后又有一个独立的块作用域。 此时,lock_guard 对象的作用域是从定义开始直到它所在的块结束(即整个函数结束,或者外部块结束),而不仅仅是在内部块中。 因此,在内部块结束后并不会被释放,而是会持续到外部块结束(即lock_guard对象被析构的地方)。 所以,从的作用域来看: 第一段代码:的作用域就是显式块的作用域,即只覆盖了 process() 调用。 第二段代码:的作用域从定义开始,直到外部块结束(比如函数结束),这可能会覆盖 process() 调用后的其他操作,导致的持有时间过长。 结论:第一段代码的作用域更准确,它确保了只保护需要保护的临界区(即process()调用)。而第二段代码会延长的持有时间,可能会降低并发性能。 因此,准确的说法是:第一段代码的作用域控制是准确的,仅保护了需要互斥的代码段。 注意:在第二段代码中,如果外部块在process()调用后还有其他操作,那么这些操作也会被保护(如果不需要保护,则是不必要的)。如果外部块没有其他操作,那么会在整个外部块结束时释放,但这样写容易导致误解,且不够精确。 示例: 代码1: { std::lock_guard<std::mutex> lock(mtx); // 在这里获取 process(); // 临界区 } // 在这里释放 代码2: std::lock_guard<std::mutex> lock(mtx); // 在这里获取 { process(); // 临界区 } // 但并不会在这里释放,因为lock_guard对象的作用域还没有结束 // 这里可能还有其他操作,也被保护 // 直到lock_guard对象的作用域结束(比如函数结束或外部块结束)才释放 因此,为了确保的作用域最小化(即只在必要的代码段上加),我们应该使用第一种方式。 §§相关问题§§: 1. 为什么使用 `std::lock_guard` 时应该尽量缩小的作用域? 2. 在同一个作用域内多次使用 `std::lock_guard` 定同一个互斥量会导致什么问题? 3. 如何避免因的作用域过大而导致的性能问题? 4. `std::lock_guard` 和 `std::unique_lock` 在作用域控制上有何不同? </think> ### 作用域精确分析:两种写法的区别与最佳实践 #### 代码解析与作用域对比 **第一种写法(正确作用域控制):** ```cpp { std::lock_guard<std::mutex> lock(mtx); // 在块作用域开头获取 process(); // 受保护的操作 } // 在此处自动释放 ``` - **作用域起点**:`{` 处开始 - **获取时刻**:`lock` 对象构造时 - **释放时刻**:`}` 处`lock`对象析构时 - **保护范围**:精确覆盖`process()`调用 **第二种写法(潜在问题):** ```cpp std::lock_guard<std::mutex> lock(mtx); // 在外部作用域获取 { process(); // 受保护的操作 } // 块结束,但未释放! // 在此作用域后续代码仍保持定 ``` - **作用域起点**:`lock`声明处开始 - **释放时刻**:外层作用域结束时(可能是函数结束) - **风险**:持有时间过长,导致不必要的线程阻塞 #### 关键区别与性能影响 | 特性 | 第一种写法 | 第二种写法 | |------|------------|------------| | **持有时间** | 最小化(仅包裹关键区) | 可能过长(覆盖外围代码) | | **并发性能** | 最优(快速释放) | 较差(保留时间过长) | | **死风险** | 低 | 中高(可能意外持有) | | **代码可维护性** | 高(作用域清晰) | 低(范围不明确) | #### 正确使用示例(带实际场景) ```cpp void process_data(std::vector<int>& data) { // 1. 非临界区操作(无需) prepare_resources(); // 2. 精确作用域(仅保护共享数据访问) { std::lock_guard<std::mutex> lock(data_mutex); for (auto& item : data) { process(item); // 耗时操作 } } // 在此释放,允许其他线程访问data // 3. 后续非临界区操作 generate_report(); // 可能耗时操作,但无需 } ``` #### 最佳实践原则 1. **最小化作用域**: ```cpp // 推荐 { std::lock_guard<std::mutex> lock(mtx); only_critical_operations(); } // 避免 std::lock_guard<std::mutex> lock(mtx); critical_operations(); non_critical_operations(); // 错误持有 ``` 2. **避免在内调用不可控函数**: ```cpp { std::lock_guard<std::mutex> lock(mtx); // 危险:外部函数可能阻塞或耗时 // external_service_call(); // 安全:仅快速本地操作 update_cache(); } ``` 3. **复杂场景使用`std::unique_lock`**: ```cpp void conditional_processing() { std::unique_lock<std::mutex> lock(mtx, std::defer_lock); if (need_processing()) { lock.lock(); // 按需定 process(); } // lock自动管理 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八月的雨季997

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值