2015-06-10 20 views
7

Buduję oprogramowanie czasu rzeczywistego, w którym mam główne nieskończone pętle na main() oraz wątki używane do odczytywania i przetwarzania danych.C++ std :: wektor niezależnych std :: thread

Jednym z problemów jest utrzymywanie wątków do wysyłania sygnałów do nich i monitorowania ich działania. Tak ułożyła ten kod:

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

To doesnt nawet skompilować, otrzymuję ten błąd:

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

Nitki areindependent, więc nie potrzebujesz zadzwonić join na głównym programie ani na każdym wątku ...

Tak, oto moje wątpliwości:

Dlaczego mój kod nie kompiluje?

Czy to jest właściwy sposób przechowywania wektora wątków?

Dzięki za pomoc ...

PS: Original code here:

+0

Jeśli planujesz zatrzymać aplikację poprawnie, powinieneś zadzwonić 'join()' zanim obiekt wątku zostanie zniszczony. Lub wywołaj 'detach()', aby odłączyć wątek od obiektu. W przeciwnym razie otrzymasz wywołanie 'terminate()' w destruktorze wątków. – gomons

+0

gomony, nie rozumiem. join() wstrzyma wykonywanie głównego wątku, a to nie jest pożądane tutaj ... – Mendes

+0

Możesz również użyć 'readThreads.emplace_back (readThread :: start, reader);', Co oznacza gomons oznacza, że ​​musisz albo dołączyć() 'lub' detach() 'instancję wątku przed wykonaniem destruktora, w przeciwnym razie wywoływana jest funkcja' zakończ() '. Ale twoje wątki wydają się być całkiem szczęśliwe, że będą działać wiecznie, więc nie jest to problemem w powyższym przykładzie. – Praetorian

Odpowiedz

11

trzeba użyć czegoś jak

readerThreads.push_back(move(th)); 

To sprawi th rvalue i spowodować konstruktor ruch na miano . Kopia ctor z thread została zaprojektowana zgodnie z projektem (patrz Anthony Williams "C++ Concurrency In Action).

+2

Tak! Teraz pamiętam to z innych odczytów .. Dzięki za pomoc ... Praca jak czar ... – Mendes

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

Obieramy to nieco.

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

Twój kod wykonuje czynność, która próbuje wprowadzić std::thread.

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

push_back jest sprawcą.

std::thread nie można skopiować - co oznaczałoby znaczy, aby skopiować wątek?

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

Tak więc obiekty std::thread mają być unikatowymi właścicielami. Pomijając zwykłe zamieszanie, pojawi się wiele bólu.

Są one jednak ruchome.

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

t1 jest no-longer prawidłowym deskryptorem wątek, wątek został opisując jest obecnie własnością t2.

Do umieszczania takich przedmiotów w pojemniku można użyć opcji std::move lub std::emplace/std::emplace_back.

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

Podczas gdy kod jest skupienie się na tej konkretnej kwestii, chciałbym zwrócić uwagę, że norma C++ deklaruje go jako błąd w destruktor wątek powoływać się natomiast wątek jest nadal załączony i nie dołączył.

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

Kiedy główne odebrane, t1. ~ Gwint() jest wywoływana, który wykrywa, że ​​wątek jest nadal załączony i nie dołączył, to podnosi wyjątek powodujący awarię wyłączania.

Musisz albo join() wątek, czekając na zakończenie, albo detach() go. Będziesz potrzebował jakiegoś sposobu, aby powiedzieć, że wątek się zatrzymuje, jeśli chcesz użyć join(), a jeśli chcesz, aby program zakończył się w środku wątku, robiąc coś jak zapisywanie danych itp., Możesz wprowadzić poważny błąd.

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

Tutaj używamy obietnicę pozwolić nić wiedzieć, kiedy nadszedł czas, aby przestać działać, ale można używać zmiennych stanu, sygnały, itp, lub nawet tylko prosty std::atomic<bool> ok_to_run { true }; przetestowanie za fałszywe.

1

Innym wariantem, który działa, jest utworzenie obiektu wątku w wywołaniu vector.push_back. Nie musisz wywoływać std :: move w tym przypadku, ponieważ jest to już wartość r (więc zostanie przeniesiona).

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

Najprostsza i najlepsza odpowiedź. – JulianSoto