2014-01-28 70 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(),但是在执行之后队列被清空了哪些函数呢?

相关问题