2017-01-20 69 views
4

我想實現一個小線程包裝程序,該線程包裝程序在線程仍處於活動狀態或線程已完成工作時提供信息。爲此,我需要將函數及其參數傳遞給線程類執行到另一個函數。我有一個簡單的實現應該工作,但不能讓它編譯,我不知道怎麼做才能使它工作。將參數傳遞給std :: thread wrapper

這裏是我的代碼:

#include <unistd.h> 
#include <iomanip> 
#include <iostream> 
#include <thread> 
#include <utility> 

class ManagedThread 
{ 
public: 
    template< class Function, class... Args> explicit ManagedThread(Function&& f, Args&&... args); 
    bool isActive() const { return mActive; } 
private: 
    volatile bool mActive; 
    std::thread mThread; 
}; 

template< class Function, class... Args> 
    void threadFunction(volatile bool& active_flag, Function&& f, Args&&... args) 
{ 
    active_flag = true; 
    f(args...); 
    active_flag = false; 
} 

template< class Function, class... Args> 
    ManagedThread::ManagedThread(Function&& f, Args&&... args): 
     mActive(false), 
     mThread(threadFunction< Function, Args...>, std::ref(mActive), f, args...) 
{ 
} 

static void func() { std::cout << "thread 1" << std::endl; } 

int main() { 
    ManagedThread mt1(func); 
    std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() << std::endl; 
    ::sleep(1); 
    std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() << std::endl; 

    return 0; 
} 

編譯器錯誤,我得到:

In file included from /usr/include/c++/5/thread:39:0, 
       from prog.cpp:4: 
/usr/include/c++/5/functional: In instantiation of 'struct std::_Bind_simple<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>': 
/usr/include/c++/5/thread:137:59: required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(volatile bool&, void (&)()); _Args = {std::reference_wrapper<volatile bool>, void (&)()}]' 
prog.cpp:28:82: required from 'ManagedThread::ManagedThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]' 
prog.cpp:35:28: required from here 
/usr/include/c++/5/functional:1505:61: error: no type named 'type' in 'class std::result_of<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>' 
     typedef typename result_of<_Callable(_Args...)>::type result_type; 
                  ^
/usr/include/c++/5/functional:1526:9: error: no type named 'type' in 'class std::result_of<void (*(std::reference_wrapper<volatile bool>, void (*)()))(volatile bool&, void (&)())>' 
     _M_invoke(_Index_tuple<_Indices...>) 
     ^

活生生的例子可以在這裏找到:https://ideone.com/jhBF1q

+0

我沒有與轉發爭論太多的經驗,但我只是某種方式得到的感覺你缺少一個['的std :: forward'(HTTP ://en.cppreference.com/w/cpp/utility/forward)call or two ...你可能想閱讀[「何時使用std :: forward轉發參數?」](http:// stackoverflow。 COM /問題/ 7257144 /時使用的-stdforward到正向參數)。 –

+0

另請注意,您可能無法獲得您期待的結果。第一次調用'mt1.isActive()之前,您創建的線程可能會啓動,運行並結束。考慮在線程函數中添加一個短暫的睡眠,然後在'main'函數中休眠一段時間。 –

+0

我也這麼認爲,但是我添加的每個std :: forward調用實際上使其變得更糟,即導致更多的編譯器錯誤。也許如果當前的問題解決了,我將需要(並且能夠)在我的函數中應用std :: forward調用,但首先我需要讓線程構造函數調用才能工作。 – Rene

回答

6

在錯誤消息,你可以看到其中的差別void (*)() vs void (&)()。這是因爲std::thread's constructor參數是std::decayed

添加也std::reff

template< class Function, class... Args> 
    ManagedThread::ManagedThread(Function&& f, Args&&... args): 
     mActive(false), 
     mThread(threadFunction< Function, Args...>, std::ref(mActive), std::ref(f), std::forward<Args>(args)...) 
{ 
} 
+0

謝謝,這個編譯!大!目前,我得到'終止調用沒有一個活躍的異常',但這是我必須研究的另一個問題。我讀到了關於衰減的參數,但我沒有意識到這會影響我的功能。我將不得不尋找如何'的std ::向前< Args>(參數)...'作品... – Rene

+0

參見[@ jotik的答案](http://stackoverflow.com/a/41758753/3893262)用於終止通話的解釋並糾正。 –

4

通過@O'Neil答案是正確的,但我想提供一個簡單的拉姆達的方法,因爲你已經標記這是C++14

template<class Function, class... Args> 
ManagedThread::ManagedThread(Function&& f, Args&&... args): 
     mActive(false), 
     mThread([&] /*()*/ { // uncomment if C++11 compatibility needed 
     mActive = true; 
     std::forward<Function>(f)(std::forward<Args>(args)...); 
     mActive = false; 
     }) 
{} 

這樣就不需要外部功能。

+0

謝謝,我也會試試這個版本! – Rene

2

O'Neil和DeiDei第一次來到這裏,據我所知他們是正確的。不過,我仍將問題的解決方案發布給您。

這裏的東西,更好地工作:

#include <atomic> 
#include <thread> 
#include <utility> 

class ManagedThread { 

public: /* Methods: */ 

    template <class F, class ... Args> 
    explicit ManagedThread(F && f, Args && ... args) 
     : m_thread(
      [func=std::forward<F>(f), flag=&m_active](Args && ... args) 
        noexcept(noexcept(f(std::forward<Args>(args)...))) 
      { 
       func(std::forward<Args>(args)...); 
       flag->store(false, std::memory_order_release); 
      }, 
      std::forward<Args>(args)...) 
    {} 

    bool isActive() const noexcept 
    { return m_active.load(std::memory_order_acquire); } 

private: /* Fields: */ 

    std::atomic<bool> m_active{true}; 
    std::thread m_thread; 

}; 

它利用lambda表達式來代替,並正確使用的std::atomic<bool>代替volatile的狀態同步,也包括相應的noexcept()符。

另請注意,底層std::thread在銷燬前未正確連接或分離,因此導致調用std::terminate()

我改寫了測試代碼,以及:

#include <chrono> 
#include <iostream> 

int main() { 
    ManagedThread mt1(
     []() noexcept 
     { std::this_thread::sleep_for(std::chrono::milliseconds(500)); }); 
    std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() 
       << std::endl; 
    std::this_thread::sleep_for(std::chrono::seconds(1)); 
    std::cout << "thread 1 active = " << std::boolalpha << mt1.isActive() 
       << std::endl; 
} 
+0

也感謝您的解決方案。我沒有故意使用原子,因爲只有一個線程更改了標誌的值,而另一個線程正在讀取它。 – Rene

+0

關於終止:哦,對,當然,我忘了我稱之爲join()的析構函數。 – Rene

+2

@Rene你還需要原子。在一般情況下,Afaik'volatile'是不夠的。看[這個答案](http://stackoverflow.com/a/29633332/3919155)。 – jotik