2014-01-28 65 views
2

我有一個名爲任務內部擁有成員std ::線程的類。總體思路是創建一個線程,隨着處理請求的到來而保持活動狀態。std ::線程導致應用程序中止與錯誤R6010

class Task 
{ 
public: 
    Task(); 
    ~Task(); 

    void start(); 
    // some funny stuff here 
protected: 
    Task(const Task& ref); 
    void main_function(); 

    std::thread m_thread; 
    // more funny stuff like queues, mutexes, etc 
} 

在函數的start()我做的:

void Task::start() 
{ 
    m_thread = std::thread(std::bind(&Task::main_function, this)); 
} 

的問題是,這一行調用abort()與運行時錯誤R6010。我讀過的地方可能是由於m_thread的析構函數在沒有前一個連接的情況下被調用引起的,但是由於線程尚未啓動,我無法加入它。

所以,我想,在試驗例和無法複製的錯誤:

我與Visual Studio 2012

UPDATE運行此。然後我取代我的啓動功能,作爲問題的功能取得這個:

void Task::start() 
{ 
    assert(!m_thread.joinable()); 
    m_thread = std::thread(&Task::main_function,this); 
} 

但我仍然得到錯誤R6010。 調用堆棧是:

msvcr110d.dll!_NMSG_WRITE(int rterrnum) Line 226 C 
msvcr110d.dll!abort() Line 62 C 
msvcr110d.dll!terminate() Line 97 C++ 
msvcp110d.dll!_Call_func(void * _Data) Line 63 C++ 

msvcr110d.dll!_callthreadstartex() Line 354 C 
msvcr110d.dll!_threadstartex(void * ptd) Line 337 C 

UPDATE2: 終於可以複製的問題。代碼如下。在main函數中調用foo()。

class Task 
{ 
public: 
    Task() : m_exitFlag(false) 
    { 
     std::cout << "constructor called" << std::endl; 
    } 

    ~Task() 
    { 
     m_lock.lock(); 
     m_exitFlag = true; 
     m_condlock.notify_all(); 
     m_lock.unlock(); 

     if (m_thread.joinable()) m_thread.join(); 
     std::cout << "destructor called" << std::endl; 
    } 

    void start() 
    { 
     std::cout << "Task start" << std::endl; 
     assert(!m_thread.joinable()); 
     m_thread = std::thread(&Task::main_function, this); 
    } 
protected: 
    void main_function() 
    { 
     std::cout << "thread started" << std::endl; 
     while(1) 
     { 
      m_lock.lock(); 
      while(m_queue.empty() && !m_exitFlag) 
       m_condlock.wait(std::unique_lock<std::mutex>(m_lock)); 

      if (m_exitFlag) 
      { 
       m_lock.unlock(); 
       std::cout << "thread exiting" << std::endl; 
       return; 
      } 

      std::function<void()> f; 
      if (!m_queue.empty()) f = m_queue.front(); 

      m_lock.unlock; 
      if (f != nullptr) f(); 
     } 
    } 
    Task(const Task&ref) { } 

    Task& operator=(const Task& ref) { 
     return *this; 
    } 
}; 
void foo() { 
    Task tk; 
    tk.start(); 
} 

我想這裏有一個競爭條件,因爲它崩潰了一些時間,其他人沒有。 一個線程位於〜Task()中的關鍵區域內,另一個位於Update1中的堆棧。

+2

你能給我們一個完整的,可編輯的例子來證明這種行爲嗎?最有可能的是,這個錯誤正是你讀的,但是很難從這個代碼中知道。 (如果你的代碼在結束之前調用'join',在'〜Task'中說,它是否仍然會出錯?) –

+0

你確定*開始不是被調用兩次嗎?試着用'assert(!m_thread.joinable())'守護它。錯誤的全文很好。不相關:'std :: bind'是不必要的,'m_thread = std :: thread(&Task :: main_function,this);'會完成同樣的事情。 – Casey

+2

您已經重載了複製構造函數,但沒有賦值運算符!請永遠不要這樣做。 – nothrow

回答

1

永遠不要直接鎖定互斥鎖。 C++提供lock_guardunique_lock等。因爲某種原因。

特別是,這部分是有問題的:

m_lock.lock(); 
while(m_queue.empty() && !m_exitFlag) 
    m_condlock.wait(std::unique_lock<std::mutex>(m_lock)); 

新建成的unique_lock將試圖鎖定已鎖定的互斥m_lock。這will cause undefined behavior如果互斥鎖是std::mutex或者如果互斥鎖是std::recursive_mutex可能死鎖。 另請注意,此行依賴於非標準的編譯器擴展,因爲您綁定了未命名的unique_lockto a non-const reference when calling wait

所以你要做的第一件事就是讓鎖定一個命名變量。然後將std::adopt_lock傳遞給鎖的構造函數,或者更好,但不要直接鎖定該互斥鎖,而是始終將其包裝在合適的鎖管理類中。

例如,

m_lock.lock(); 
m_exitFlag = true; 
m_condlock.notify_all(); 
m_lock.unlock(); 

成爲

{ 
    std::lock_guard<std::mutex> lk(m_lock); 
    m_exitFlag = true; 
    m_condlock.notify_all(); 
} // mutex is unlocked automatically as the lock_guard goes out of scope 

這樣做,如果有異常的關鍵部分內拋出你不會泄漏鎖額外的好處。

0

看起來你是從隊列的front()中取出一個函數,然後在解鎖後運行該函數,而不是先用pop()從隊列中刪除它。這是你的意圖嗎?在這種情況下,下一個隨機線程也可能獲得相同的功能並同時運行它。這些函數是線程安全的嗎?另外,你檢查queue.empty(),但是在執行之後隊列被清空了哪些函數呢?

相關問題