简体   繁体   English

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

[英]C++ std::vector of independent std::threads

I'm building a real time software where I have a main infinite loops on main() and threads used to read and process data.我正在构建一个实时软件,其中我在main()上有一个主要的无限循环,并且用于读取和处理数据的线程。

One of the issues is keeping a std::vector of running threads to send signals to them and to monitor execution.问题之一是保持运行线程的std::vector以向它们发送信号并监视执行。 So I put together this code:所以我把这段代码放在一起:

#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;
}

It doesn´t even compile, getting this error:它甚至没有编译,得到这个错误:

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;
     ^

The threads are independent, so I don't need to call join on the main program nor on any thread...线程是独立的,所以我不需要在主程序或任何线程上调用join ...

So, here are my doubts:所以,这是我的疑问:

Why my code does not compile?为什么我的代码无法编译?

Is that the correct way to store the vector of threads ?这是存储线程向量的正确方法吗?

Thanks for helping...感谢您的帮助...

PS: Original code here : PS:这里的原始代码

You need to use something like你需要使用类似的东西

readerThreads.push_back(move(th));

This will make th an rvalue, and cause the move ctor to be called.这将使th成为右值,并导致调用移动 ctor。 The copy ctor of thread was disabled by design (see Anthony Williams' C++ Concurrency In Action ). thread的复制 ctor 被设计禁用(请参阅 Anthony Williams 的C++ Concurrency In Action )。

/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)...); }

Lets peel this back a bit.让我们把它剥开一点。

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

Your code is doing something that attempts to introduce a std::thread .您的代码正在做一些尝试引入std::thread的事情。

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

push_back is the culprit. push_back是罪魁祸首。

std::thread is not copyable - what would it mean to copy a thread? std::thread不可复制 - 复制线程意味着什么?

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

So instances of std::thread objects are intended to be unique owners.因此std::thread对象的实例旨在成为唯一的所有者。 Aside from the simple confusion, much pain would ensue.除了简单的混乱之外,还会有很多痛苦。

They are, however, movable.然而,它们是可移动的。

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

t1 is no-longer a valid thread descriptor, the thread it was describing is now owned by t2 . t1不再是有效的线程描述符,它所描述的线程现在归t2所有。

For placing such things into a container you can either use std::move or std::emplace / std::emplace_back .要将这些东西放入容器中,您可以使用std::movestd::emplace ::emplace / std::emplace_back

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

While your code is focusing on this particular issue, let me point out that the C++ standard declares it as an error for a thread destructor to be invoked while a thread is still attached and not joined.虽然您的代码专注于这个特定问题,但让我指出 C++ 标准将其声明为在线程仍处于连接状态且未连接时调用线程析构函数的错误。

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

When main terminates, t1.~thread() is called, which detects that the thread is still attached and not joined, this raises an exception causing a shutdown crash.当 main 终止时,t1.~thread() 被调用,它检测到线程仍然是附加的并且没有加入,这会引发一个导致关闭崩溃的异常。

You either need to join() the thread, waiting for it to terminate running, or detach() it.您要么需要join()线程,等待它终止运行,要么需要detach()它。 You'll need some way to tell the thread to stop if you want to use join() and if you detach() the program may exit in the middle of the thread doing something like writing data etc, you may introduce a serious bug.如果你想使用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();
}

Here we use a promise to let the thread know when it's time to stop running, but you could use condition variables, signals, etc, or even just a simple std::atomic<bool> ok_to_run { true };这里我们使用 promise 让线程知道何时停止运行,但您可以使用条件变量、信号等,甚至只是一个简单的std::atomic<bool> ok_to_run { true }; that you test for false.你测试为假。

Another variant that works is to create your thread object in the vector.push_back call.另一个可行的变体是在 vector.push_back 调用中创建你的线程对象。 No need to call std::move in this case because its already an rvalue (thus it will be be moved).在这种情况下不需要调用 std::move 因为它已经是一个右值(因此它将被移动)。

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

This is guarantee that every thread will be created in vector's storage.这是保证每个线程都将在向量的存储中创建。 No object byte will be copied.不会复制任何对象字节。

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

In common case this not the same as @Seth suggested.通常情况下,这与@Seth 建议的不同。 But in current case it is 99% the same.但在目前的情况下,它是 99% 相同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM