簡體   English   中英

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

[英]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成為右值,並導致調用移動 ctor。 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)...); }

讓我們把它剝開一點。

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 ::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 }; 你測試為假。

另一個可行的變體是在 vector.push_back 調用中創建你的線程對象。 在這種情況下不需要調用 std::move 因為它已經是一個右值(因此它將被移動)。

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

這是保證每個線程都將在向量的存儲中創建。 不會復制任何對象字節。

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

通常情況下,這與@Seth 建議的不同。 但在目前的情況下,它是 99% 相同的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM