独立 std::threads 的 C++ std::vector

Posted

技术标签:

【中文标题】独立 std::threads 的 C++ std::vector【英文标题】:C++ std::vector of independent std::threads 【发布时间】:2015-08-26 09:45:09 【问题描述】:

我正在构建一个实时软件,我在 main() 上有一个主要的无限循环,以及用于读取和处理数据的线程。

其中一个问题是保持std::vector 正在运行的线程向它们发送信号并监视执行。所以我把这段代码放在一起:

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>

namespace readerThread 

    void start(int id)
    
        while (1)
        
            std::cout << "Reader " << id << " running..." <<  std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        
    




int main() 


        int readers[] =  1, 2, 3 ;

        std::vector<std::thread> readerThreads;

        for (int &reader : readers)
        
            std::thread th(readerThread::start, reader);
            readerThreads.push_back(th);
        

        while(true)
        
            std::cout << "Waiting..." << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(10000));
        

        return 0;

它甚至没有编译,得到这个错误:

In file included from /usr/local/include/c++/5.1.0/x86_64-unknown-linux-gnu/bits/c++allocator.h:33:0,
                 from /usr/local/include/c++/5.1.0/bits/allocator.h:46,
                 from /usr/local/include/c++/5.1.0/string:41,
                 from /usr/local/include/c++/5.1.0/bits/locale_classes.h:40,
                 from /usr/local/include/c++/5.1.0/bits/ios_base.h:41,
                 from /usr/local/include/c++/5.1.0/ios:42,
                 from /usr/local/include/c++/5.1.0/ostream:38,
                 from /usr/local/include/c++/5.1.0/iostream:39,
                 from main.cpp:1:
/usr/local/include/c++/5.1.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = const std::thread&; _Tp = std::thread]':
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:256:4:   required from 'static std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = const std::thread&; _Alloc = std::allocator<std::thread>; std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > = void]'
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:402:16:   required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = const std::thread&; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/local/include/c++/5.1.0/bits/stl_vector.h:917:30:   required from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::thread; _Alloc = std::allocator<std::thread>; std::vector<_Tp, _Alloc>::value_type = std::thread]'
main.cpp:37:30:   required from here
/usr/local/include/c++/5.1.0/ext/new_allocator.h:120:4: error: use of deleted function 'std::thread::thread(const std::thread&)'
   ::new((void *)__p) _Up(std::forward<_Args>(__args)...); 
    ^
In file included from main.cpp:4:0:
/usr/local/include/c++/5.1.0/thread:126:5: note: declared here
     thread(const thread&) = delete;
     ^

线程是独立的,所以我不需要在主程序或任何线程上调用join...

所以,这是我的疑问:

为什么我的代码无法编译?

这是存储线程向量的正确方法吗?

感谢您的帮助...

PS:Original code here:

【问题讨论】:

如果您打算正确停止您的应用程序,您应该在线程对象销毁之前调用join()。或调用detach() 将线程与对象分离。否则你会在线程析构函数中得到terminate() 调用。 gomons,没明白你的意思。 join() 将持有主线程执行,这不是这里所希望的...... 你也可以使用readerThreads.emplace_back(readerThread::start, reader);,gomons的意思是你必须要么join()要么detach()一个线程实例在其析构函数执行之前,否则terminate()被调用。但是您的线程似乎很乐意永远运行,因此在上面的示例中这不是问题。 【参考方案1】:

你需要使用类似的东西

readerThreads.push_back(move(th));

这将使th 成为右值,并导致调用移动ctor。 thread 的复制 ctor 设计为 disabled(参见 Anthony Williams 的 C++ Concurrency In Action)。

【讨论】:

是的!现在我记得从其他阅读中...感谢您的帮助...像魅力一样工作...【参考方案2】:
/usr/local/include/c++/5.1.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = const std::thread&; _Tp = std::thread]':
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:256:4:   required from 'static std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = const std::thread&; _Alloc = std::allocator<std::thread>; std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > = void]'
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:402:16:   required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = const std::thread&; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/local/include/c++/5.1.0/bits/stl_vector.h:917:30:   required from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::thread; _Alloc = std::allocator<std::thread>; std::vector<_Tp, _Alloc>::value_type = std::thread]'
main.cpp:37:30:   required from here
/usr/local/include/c++/5.1.0/ext/new_allocator.h:120:4: error: use of deleted function 'std::thread::thread(const std::thread&)'
   ::new((void *)__p) _Up(std::forward<_Args>(__args)...); 

让我们把它剥开一点。

error: use of deleted function 'std::thread::thread(const std::thread&)'

您的代码正在尝试引入std::thread

required from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&)

push_back 是罪魁祸首。

std::thread 不可复制 - 复制线程意味着是什么意思?

std::thread t1([]());
std::thread t2 = t1;

因此,std::thread 对象的实例旨在成为唯一的所有者。除了简单的混乱之外,还会带来很多痛苦。

但是,它们是可移动的。

std::thread t1([]());
std::thread t2 = std::move(t1);

t1 不再是有效的线程描述符,它所描述的线程现在归t2 所有。

要将这些东西放入容器中,您可以使用std::movestd::emplace/std::emplace_back

std::vector<std::thread> threads;
threads.push_back(std::move(std::thread([]())));
threads.emplace_back([]());

虽然您的代码专注于这个特定问题,但让我指出,C++ 标准将其声明为在线程仍处于连接状态且未加入时调用线程析构函数的错误。

int main() 
    std::thread t1([]() while (true)  std::this_thread::yield();  ;

当 main 终止时,t1.~thread() 被调用,它检测到线程仍然被连接并且没有被加入,这会引发一个导致关闭崩溃的异常。

您要么需要join() 线程,等待它终止运行,要么需要detach() 它。如果你想使用join(),你需要一些方法来告诉线程停止,如果你detach(),程序可能会在线程中间退出,执行诸如写入数据等操作,你可能会引入一个严重的错误。

#include <thread>
#include <chrono>
#include <future>

int main () 
  std::promise<void> cnx_promise;
  std::shared_future<void> cnx_future;

  std::thread t1([cnx_future]() 
      while (cnx_future.valid()) 
        std::this_thread::yield();
      
  );

  std::this_thread::sleep_for(std::chrono::seconds(1));

  cnx_promise.set_value();

  t1.join();

这里我们使用 promise 让线程知道何时停止运行,但您可以使用条件变量、信号等,甚至只是一个简单的 std::atomic&lt;bool&gt; ok_to_run true ; 来测试是否为假。

【讨论】:

【参考方案3】:

另一个可行的变体是在 vector.push_back 调用中创建你的线程对象。在这种情况下不需要调用 std::move 因为它已经是一个右值(因此它将被移动)。

for (int &reader : readers)
    readerThreads.push_back(std::thread(readerThread::start, reader));

【讨论】:

最简单最好的答案。【参考方案4】:

这是保证每个线程都将在向量的存储中创建。不会复制任何对象字节。

for (int &reader : readers)
    readerThreads.emplace_back( readerThread::start, reader );

通常情况下,这与@Seth 建议的不同。但在当前情况下,99% 相同。

【讨论】:

以上是关于独立 std::threads 的 C++ std::vector的主要内容,如果未能解决你的问题,请参考以下文章

多个 std::threads 和主程序执行出现问题

可以从 Windows DLL 中的全局变量创建/销毁 std::threads 吗?

C++11 中 std::threads 向量的问题计算字母和单词的二元组

如何放弃或取消 std::thread

C++ std::string 构造函数

C++ - 使用 std::async 时显示进度条