关于std::thread以及std::condition_variable的一些细节备忘

  也算是看过不少多线程相关的资料了,但是一直对于其中的一些细节没有太好的把握,比如std::thread线程真正开始运行的时机,比如join、detch等真正的作用。

跟着《Cplusplus Concurrency In Action_Practical Multithreading》又过了一遍相关的细节,下面记录一下一些个人所获得的收获。

std::thread真正开始运行的时机

下面是我尝试写的一个基于条件变量和互斥量的生产者消费者模型的Demo,就从这里开始说起

#include<iostream>
#include<thread>
#include<unistd.h>
#include<vector>
#include<mutex>
#include<condition_variable>
#include<cmath>

std::vector<int> resource;
std::mutex m;
std::condition_variable cv;

void thread_procedure(){
    while(true){
        int n;
        while(true){
            std::unique_lock<std::mutex> ul(m);
            cv.wait(ul,[&]{return !resource.empty();});
            if(!resource.empty()) {
                n = resource.back();
                resource.pop_back();
                break;
            }
        }

        int res = 0;
        for(int i = 0; i<n; ++i){
            //sleep(2);
            res+=i;
        }
        std::lock_guard<std::mutex> ll(m);
        std::cout<<"This is from Thread "<<std::this_thread::get_id()<<", "<<"The sum of 0+1+2+...+"<<n<<" is "<<res<<std::endl;
    }
}

int main(){
    std::thread tt[5];
    for(int i = 0; i<5; ++i){
        tt[i] = std::thread(thread_procedure);
    }

    for(int i = 0; i<5; ++i){
        tt[i].join();
    }
    while(true){
        std::lock_guard<std::mutex> lg(m);
        resource.push_back(rand()%100);
        cv.notify_one();
    }
}

这段代码使用了一个std::mutex和一个std::condition_variable控制相应的线程,尝试实现一个简单的打印功能。

但在运行时该代码会卡在生产者处,即join代码之后的while循环不会运行下去。。。

这里几乎就涉及了std::thread线程库里面对于线程启动的机制以及join的真正语义了。

下面是一段GNU对于std::thread的实现代码:

class thread
{
    ...
public:
    thread() noexcept = default;
    thread(thread&) = delete;
    thread(const thread&) = delete;
    thread(thread&& __t) noexcept
    { swap(__t); }
    template<typename _Callable, typename... _Args>
    explicit thread(_Callable&& __f, _Args&&... __args)
    {
        _M_start_thread(_M_make_routine(std::__bind_simple(
        std::forward<_Callable>(__f),
        std::forward<_Args>(__args)...)));
     }
    ...
};

可以看到thread的构造函数传入了一个_Callable可调用对象以及相关的参数,然后使用了std::__bind_simple进行了包装,相当于std::bind,然后使用_M_start_thread直接使用平台相关线程实现开启了这个线程!

从这里我们可以看出在每个std::thread构造完成的时候新线程就已经开启了!

而join函数的作用就是等待join的线程执行结束,在join返回之后继续运行后续代码。

这样上面的代码会卡住也就理所应当了,在开启新线程之后资源池就用完了,然后启动的线程都阻塞在条件变量上面,而后续的while循环里面的生产过程则是由于join函数在等待已经开启的线程结束而无法运行。

整个程序就会在所有线程都处于阻塞状态下停在那里。

而解决这个问题的方法倒也简单,另开一个生产者线程就行,如下代码:

#include<iostream>
#include<thread>
#include<unistd.h>
#include<vector>
#include<mutex>
#include<condition_variable>
#include<cmath>

std::vector<int> resource;
std::mutex m;
std::condition_variable cv;

void thread_procedure(){
    while(true){
        int n;
        while(true){
            std::unique_lock<std::mutex> ul(m);
            cv.wait(ul,[&]{return !resource.empty();});
            if(!resource.empty()) {
                n = resource.back();
                resource.pop_back();
                break;
            }
        }

        int res = 0;
        for(int i = 0; i<n; ++i){
            res+=i;
        }
        std::lock_guard<std::mutex> ll(m);
        std::cout<<"This is from Thread "<<std::this_thread::get_id()<<", "<<"The sum of 0+1+2+...+"<<n<<" is "<<res<<std::endl;
    }
}

void producer(){
    while(true){
        std::lock_guard<std::mutex> lg(m);
        resource.push_back(rand()%100);
        cv.notify_one();
    }
}

int main(){
    std::thread tt[6];
    for(int i = 0; i<5; ++i){
        tt[i] = std::thread(thread_procedure);
    }

    tt[5] = std::thread(producer);

    for(int i = 0; i<6; ++i){
        tt[i].join();
    }
}

这种情况下,所有工作线程都在join函数调用之前就开启了,也就不会存在上述问题了。

原文地址:https://www.cnblogs.com/J1ac/p/9813955.html

时间: 2024-10-10 18:44:36

关于std::thread以及std::condition_variable的一些细节备忘的相关文章

C++——多线程编程(一)std::thread

(一)与C++11多线程相关的头文件 C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< atomic> ,< thread>,< mutex>,< condition_variable>和< future>. ?< atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数. ?< thread>

C++11 并发指南------std::thread 详解

参考: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E8%AF%A6%E8%A7%A3 本节将详细介绍 std::thread 的用法. std::thread 在 <thread> 头文件中声明,因此使用 std::thread 需包含 <thread> 头文件. &

std::thread

std::thread为C++11的线程类,使用方法和boost接口一样,非常方便.C++11的std::thread解决了boost::thread中构成参数限制的问题. #include <thread>         // std::thread, std::thread::id, std::this_thread::get_id  #include <chrono>         // std::chrono::seconds void threadfun1(){}vo

[原]std::thread总结

原 总结 C++11 thread 缘起 std::thread 类定义 各个成员函数的简单介绍 例子 参考资料 缘起 从C++11开始提供了线程的支持,终于可以方便的编写跨平台的线程代码了.除了std::thread类,还提供了许多其它便利同步的机制,本篇总结是C++11学习笔记系列的首篇总结. std::thread std::thread定义在<thread>中,提供了方便的创建线程的功能. 类定义 class thread {  public:  thread() noexcept; 

C++ std::thread概念介绍

C++ 11新标准中,正式的为该语言引入了多线程概念.新标准提供了一个线程库thread,通过创建一个thread对象来管理C++程序中的多线程. 本文简单聊一下C++多线程相关的一些概念及thread的基本用法. 0. 并行执行 程序并行执行两个必要条件: 多处理器(multiple processors)or 多核处理器(multicore processors) 软件并行 软件并发执行可分为两大类: 多线程并发  (同一个进程的多个线程并行): 多进程并发  (不同进程并行): 对于多线程

c++11 使用异步编程std::async和std::future

先说明一点:std::asyanc是std::future的高级封装, 一般我们不会直接使用std::futrue,而是使用对std::future的高级封装std::async. 下面分别说一下. 一.std::async基本用法 std::future可以从异步任务中获取结果,一般与std::async配合使用,std::async用于创建异步任务,实际上就是创建一个线程执行相应任务. std::async就是异步编程的高级封装,封装了std::future的操作,基本上可以代替std::t

std::thread(2)

个线程都有一个唯一的 ID 以识别不同的线程,std:thread 类有一个 get_id() 方法返回对应线程的唯一编号,你可以通过 std::this_thread 来访问当前线程实例,下面的例子演示如何使用这个 id: #include <thread> #include <iostream> #include <vector> void hello(){ std::cout << "Hello from thread " <

C++11多线程std::thread的简单使用

转自:http://blog.csdn.net/star530/article/details/24186783 在cocos2dx 2.0时代,我们使用的是pthread库,是一套用户级线程库,被广泛地使用在跨平台应用上.但在cocos2dx 3.0中并未发现有pthread的支持文件,原来c++11中已经拥有了一个更好用的用于线程操作的类std::thread.cocos2dx 3.0的版本默认是在vs2012版本,支持c++11的新特性,使用std::thread来创建线程简直方便. 下面

用std::thread替换实现boost::thread_group

thread_group是boost库中的线程池类,内部使用的是boost::thread. 随着C++ 11标准的制定和各大编译器的新版本的推出(其实主要是VS2012的推出啦……),本着能用标准库就用标准库的指导原则,决定把项目中多线程相关的部分代码从boost::thread迁移到std::thread. thread的迁移本身很简单,毕竟stl的很多功能是直接从boost发展而来的,基本上就是改一下头文件和名称空间的问题,例外是thread_group,thread_group是boos