独立std::线程的C++std::向量
C++ std::vector of independent std::threads
我正在构建一个实时软件,其中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:此处的原始代码:
您需要使用类似的东西
readerThreads.push_back(move(th));
这将使th
成为一个右值,并导致调用move ctor。thread
的复制ctor已被设计禁用(请参阅Anthony Williams的C++并发操作(。
另一种可行的变体是在vector.push_back调用中创建线程对象。在这种情况下不需要调用std::move,因为它已经是一个右值(因此它将被移动(。
for (int &reader : readers)
readerThreads.push_back(std::thread(readerThread::start, reader));
/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::move
或std::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<bool> ok_to_run { true };
来测试是否为false。
这保证了每个线程都将在向量的存储中创建。不会复制任何对象字节。
for (int &reader : readers)
readerThreads.emplace_back( readerThread::start, reader );
在常见情况下,这与@Seth建议的不同。但在目前的情况下,它是99%相同的。
- std::向量与传递值的动态数组
- 如何在旧c++中初始化const-std向量
- 如何检查两个 std::向量在小于 O(n) 的时间复杂度内是否相等
- 当键值是 std 向量时,为什么使用 at in C++ 访问映射值如此缓慢?
- 如何初始化 std::向量的映射?
- 迭代时将指向differents类型的指针保存在std::向量中
- std::向量迭代器和调整大小/保留的奇怪/有趣行为
- yaml-cpp到std::向量迭代的怪异行为
- 故意泄漏std::向量的内存
- 调整STD ::向量的大小是否可以降低其能力
- std ::向量距离函数如何给出比.size()更高的值
- C++ std::向量插入两个元素替代算法失败
- 错误:数字常数之前的预期无限制ID:std :: array和std ::向量大小分配
- 在我提供的此示例中,如何将2维std ::向量的逻辑更改为具有向量[row] [col] [col] [col] [co
- GUI滑块的动态数量,该数量更新具有回调中值的std ::向量
- 是一个std ::向量线程,我的目的是安全的
- 将具有std ::向量的C 函数称为Julia的输入和输出参数
- 将std ::向量作为指针参考
- STD ::向量如何调整其内部缓冲区大小
- 在C 中,是否有可能在不兼容类型的std ::向量对象之间传输不同类型的缓冲区