2013-11-21 15 views
6

Chcę ustawić wywołanie funkcji (lub funkcji lambda), aby stało się automatycznie po wyjściu bieżącego wątku, ale nie widzę żadnej możliwości to działa z std::thread, chyba że przejmiemy całe zadanie tworzenia wątku lub ręcznie upewnię się, że każdy wątek wywołuje określoną funkcję, którą zawsze dostarczam jako ostatnią operację.Wywoływanie funkcji automatycznie na wyjściu std :: thread w C++ 11

Zasadniczo, chcę funkcji, której prototyp może przypominać coś takiego:

on_thread_exit(const std::function<void()> &func); 

Które wykonać cokolwiek setup było konieczne w celu zapewnienia, że ​​dana funkcja została automatycznie wywoływana, gdy wątek, który nazywa on_thread_exit ostatecznie kończy, a bez potrzeby wywoływania jawnie konkretnych funkcji podczas tworzenia lub kończenia wątku.

+0

to ma znaczenie który wątek wywołuje wywołania zwrotnego? Czy można to zrobić za pomocą wątku pomocnika, zaraz po wyjściu wątku wyzwalającego? –

Odpowiedz

19

Możesz to zrobić używając pamięci masowej thread_local, ponieważ C++ 11 powinien wywoływać destruktory po zakończeniu wątku. Musisz się upewnić, że masz kompilator, który obsługuje to poprawnie.

#include <stack> 
#include <function> 

void on_thread_exit(std::function<void()> func) 
{ 
    class ThreadExiter 
    { 
    std::stack<std::function<void()>> exit_funcs; 
    public: 
    ThreadExiter() = default; 
    ThreadExiter(ThreadExiter const&) = delete; 
    void operator=(ThreadExiter const&) = delete; 
    ~ThreadExiter() 
    { 
     while(!exit_funcs.empty()) 
     { 
     exit_funcs.top()(); 
     exit_funcs.pop(); 
     } 
    } 
    void add(std::function<void()> func) 
    { 
     exit_funcs.push(std::move(func)); 
    } 
    }; 

    thread_local ThreadExiter exiter; 
    exiter.add(std::move(func)); 
} 

Zasadniczo ta funkcja tworzy obiekt thread_local z powyższej klasy. Jest to w zasadzie statyczne, z wyjątkiem jest niszczone, gdy wątek wychodzi. Wywoływany, wywołuje funkcję na wektorze, a po jej zniszczeniu uruchamia funkcje.

Możesz go użyć, wywołując on_thread_exit() z dowolnego wątku, który utworzy obiekt wyjściowy, który będzie uruchamiał twoją funkcję w odwrotnej kolejności, w której został wstawiony do kolejki wątków (możesz dowolnie modyfikować według własnego uznania).

+0

Bardzo ładne rozwiązanie. – Oakdale

+0

możesz chcieć dodać try/catch do destruktora, ponieważ funkcja zapisana może rzutować –

1

Można użyć BOOST_SCOPE_EXIT

Do głównego wątku:

void on_thread_exit(); 

void main(int argc, char** argv) 
{ 
    BOOST_SCOPE_EXIT() // may be one arg, at least, is required 
    { 
     on_thread_exit(); 
    }BOOST_SCOPE_EXIT_END 

    ... 
} 

zostawiam Ci ekstrapolować żadnej nici!

4

Oto uproszczony/wersja skrócona w oparciu o rozwiązania Dave S, że używa więcej C++ 11 elementy o następujących właściwościach:

  • Od ThreadExiter jest używany tylko raz, możemy połączyć deklaracji zmiennej, należy unikać public/private (class do struct) i wyjąć operatora konstruktor kopii/zadanie
  • Wymienić std::stack o zakresie oparty na pętli i std::deque
  • Dodać do variabl użytkownik e bezpośrednio zamiast metody add()

Zauważ, że to niszczy funkcje oddzwonienia z std::deque dopiero po wszystkie zostały nazwane, co może lub nie może być to, co chcesz - jeśli wymagają obiekt funkcyjny, aby być niszczone po wywołaniu (i przed wywołaniem jakichkolwiek innych obiektów funkcji), użyj stosu i pop elementów, jak w rozwiązaniu Dave'a).

Kod:

#include <functional> 
#include <deque> 

void 
on_thread_exit(std::function<void()> func) 
{ 
    thread_local struct ThreadExiter { 
     std::deque<std::function<void()>> callbacks; 

     ~ThreadExiter() { 
      for (auto &callback: callbacks) { 
       callback(); 
      } 
     } 
    } exiter; 

    exiter.callbacks.emplace_front(std::move(func)); 
} 
Powiązane problemy