互斥锁解锁失败的原因奇怪

8

我正在尝试使用一些套接字、线程和互斥锁。我的问题涉及线程和互斥锁:

int ConnectionHandler::addNewSocket(){

    this->connectionList_mutex.lock();
    std::cout << "test1" << std::endl;
    this->connectionList_mutex.unlock();

    return 0;
}

int ConnectionHandler::main(){
    while(true){
        this->connectionList_mutex.lock();
        std::cout << "test2" << std::endl;
        this->connectionList_mutex.unlock();
    }

}`

主要功能在一个线程中运行,而addNewSocket是由另一个线程调用的。 问题在于,当addNewSocket被第二个线程调用一次时,主线程下一次解锁将失败,并出现奇怪的“信号SIGABRT”。 我已经花了两天时间来解决这个问题,但可惜没有成功。 我希望你能帮助我。
编辑:ConnectionHandler是一个类,其中包含connectionList_mutex作为成员。
编辑:有时我还会收到此错误:“Assertion failed: (ec == 0), function unlock, file / SourceCache / libcxx / libcxx-65.1 / src / mutex.cpp,line 44。”但它是随机发生的。
编辑:这是整个类(缩减到最低限度,应该在一定程度上独立于上下文,但如果我将它放在客户端连接后面就会崩溃,并且如果我将它放在启动后面,它会工作:
class ConnectionHandler{
public:
    ConnectionHandler();
    int addNewSocket();
private:
    int main();
    static void start(void * pThis);

    std::mutex connectionList_mutex;
};

ConnectionHandler::ConnectionHandler(){
    std::thread t(&this->start, this);
    t.detach();
}
void ConnectionHandler::start(void * pThis){
    ConnectionHandler *handlerThis;
    handlerThis = (ConnectionHandler *)pThis;
    handlerThis->main();
}


int ConnectionHandler::addNewSocket(){

    this->connectionList_mutex.lock();
    std::cout << "test1" << std::endl;
    this->connectionList_mutex.unlock();

    return 0;
}

int ConnectionHandler::main(){
    while(true){
        this->connectionList_mutex.lock();
        std::cout << "test2" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        this->connectionList_mutex.unlock();

    }

}

为什么要标记std?你的mutex是std::mutex还是其他什么? - Manoj R
好吧,也许我很蠢,但现在(我编写了一小段代码)它能运行,我只需找出为什么它在我的实际程序环境下不起作用。 - Jonas Eschmann
2
这可能对您的问题没有帮助,但应该考虑使用RAII包装器(lock_guardunique_lock)来锁定互斥量,而不是手动锁定和解锁。这样,如果块提前退出或抛出异常,它就不会被永久锁定。 - Mike Seymour
3
它们是标准的 C++11 类,因此它们应该可以在您可以使用 std::mutex 的任何地方进行移植。 - Mike Seymour
@jstine:是的,考虑到它创建了自己的分离线程并且没有关闭的方法,几乎可以确定这个对象(以及相关的互斥锁)将在线程退出之前被销毁,这就是为什么我给出了我所给的答案。 - Omnifarious
显示剩余6条评论
1个回答

6

我猜您的ConnectionHandler对象在某个地方被销毁了。此外,您定义了ConnectionHandler::start的方式很愚蠢。

首先,ConnectionHandler::start应该这样定义:

void ConnectionHandler::start(ConnectionHandler * pThis){
    pThis->main();
}

C++11中的::std::thread类完全能够保留函数参数的类型,因此无需使用void *

其次,加入以下代码:

void ConnectionHandler::~ConnectionHandler(){
    const void * const meptr = this;
    this->connectionList_mutex.lock();
    ::std::cout << "ConnectionHandler being destroyed at " << meptr << ::std::endl;
    this->connectionList_mutex.unlock();
}

并将构造函数更改为:

ConnectionHandler::ConnectionHandler(){
    const void * const meptr = this;
    ::std::cout << "ConnectionHandler being created at " << meptr << ::std::endl;
    std::thread t(&this->start, this);
    t.detach();
}

这将展示你 ConnectionHandler 对象何时被销毁。我猜测你的代码在分离线程仍在运行时销毁了该对象。 meptr 的作用是因为 operator << 有一个重载函数可以打印出指针值。打印出 this 的指针值可以让你匹配构造函数和析构函数的调用,如果你正在创建多个 ConnectionHandler 对象。 编辑: 既然证明我的猜测是正确的,这是我建议你编写 play ConnectionHandler 类的方式:
#include <iostream>
#include <atomic>
#include <thread>
#include <chrono>
#include <mutex>

class ConnectionHandler {
 public:
   ConnectionHandler();
   ~ConnectionHandler();
   ConnectionHandler(const ConnectionHandler &) = delete;
   const ConnectionHandler &operator =(const ConnectionHandler &) = delete;

   int addNewSocket();

 private:
   int main();
   static void start(ConnectionHandler * pThis);

   ::std::mutex connectionList_mutex;
   volatile ::std::atomic_bool thread_shutdown;
   ::std::thread thread;
};

ConnectionHandler::ConnectionHandler()
     : thread_shutdown(false), thread(&this->start, this)
{
}

ConnectionHandler::~ConnectionHandler()
{
   thread_shutdown.store(true);
   thread.join();
}

void ConnectionHandler::start(ConnectionHandler * pThis){
   pThis->main();
}

int ConnectionHandler::addNewSocket(){
   ::std::lock_guard< ::std::mutex> lock(connectionList_mutex);
   ::std::cout << "test1" << ::std::endl;

   return 0;
}

int ConnectionHandler::main(){
   while(!thread_shutdown.load()){
      ::std::lock_guard< ::std::mutex> lock(connectionList_mutex);
      ::std::cout << "test2" << ::std::endl;
      ::std::this_thread::sleep_for(::std::chrono::milliseconds(100));

   }
   return 0;
}

1
第一句:非常感谢,你们在stackoverflow的团队真是太棒了!希望有一天我也能像你们一样有足够的技能去帮助别人。第二句:现在我知道ConnectionHandler在创建后就被销毁了,我开始调查这个问题了。如果没有你们的提示,我可能已经放弃了这个项目,因为我已经试图修复这个错误一个星期了 :) - Jonas Eschmann
好的,我找到了错误:我在一个函数内创建了ConnectionHandler,一旦函数结束,实例就被销毁了。根据这个链接,我已经想出来了,我必须创建一个指向使用“new”运算符创建的处理程序的指针(ConnectionHandler * newHandler = new ConnectionHandler;)。在这种情况下(如我所理解的那样),指针被销毁,但ConnectionHandler的实例仍然留在内存中。再次感谢您的帮助! - Jonas Eschmann
好的,我会尽力更好地解释一下 :). 我有一个具有主函数的Acceptor和一个具有主函数的ConnectionHandler。它们各自都有一个线程通过其主函数运行。当客户端连接(且没有ConnectionHandler)时,Acceptor将创建一个ConnectionHandler并传递连接。现在ConnectionHandler负责连接。一旦它注意到客户端已断开连接,ConnectionHandler的主循环将被中断,并且ConnectionHandler线程将通过Acceptor的成员函数通知Acceptor。 - Jonas Eschmann
将指向自身(ConnectionHandler)的指针作为参数传递,这样ConnectionHandler的线程就会跳转到Acceptor的范围内,然后销毁ConnectionHandler。 - Jonas Eschmann
@sh4kesbeer: 当你进行这样的操作时,你需要非常仔细地考虑线程交互的方式。 - Omnifarious
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接