【问题标题】:Mutex unlock fails strangely互斥解锁奇怪地失败了
【发布时间】:2012-12-13 16:16:09
【问题描述】:

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

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();
    }

}`

main 函数在一个线程中运行,而 addNewSocket 被另一个线程调用。问题是,当 addNewSocket 被调用一次(由第二个线程)时,线程 1(主)的下一次解锁将失败并出现一个奇怪的“信号 SIGABRT”。我现在已经为此工作了两天,但遗憾的是我没有设法解决它。我希望你能帮助我。

编辑:ConnectionHandler 是一个类,它有 connectionList_mutex 作为成员。

编辑:有时我也会收到此错误:“断言失败:(ec == 0),函数解锁,文件 /SourceCache/libcxx/libcxx-65.1/src/mutex.cpp,第 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 还是什么?
  • 是的,互斥体和线程都是c++11 std
  • 好吧,也许我很愚蠢,但现在(我编写了一个小代码)有效,我只需要弄清楚为什么它在我的真实程序的上下文中不起作用
  • 它可能对您的问题没有帮助,但应该考虑使用 RAII 包装器(lock_guardunique_lock)来锁定互斥锁,而不是手动锁定和解锁。这样,如果块提前退出或抛出异常,它就不会永远锁定。
  • @sh4kesbeer:它们是标准的 C++11 类,因此它们应该可以移植到任何你可以使用 std::mutex 本身的地方。

标签: c++ xcode multithreading mutex std


【解决方案1】:

我的猜测是您的 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 &lt;&lt; 有一个用于打印指针值的 void * 的重载。如果您正在创建多个 ConnectionHandler 对象,打印出 this 的指针值将允许您匹配对构造函数和析构函数的调用。

编辑: 既然我是对的,那么我建议你编写 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;
}

【讨论】:

  • 首先:非常感谢,stackoverflow 的你们都很棒!希望有一天我能像你一样有足够的技能来帮助别人。第二:现在我知道 ConnectionHandler 在创建后立即被销毁,我开始对此进行调查,如果没有您的善意暗示,我可能会放弃这个项目,因为我已经尝试修复错误一周了:)
  • 好的,我发现了错误:我已经在一个函数中创建了 ConnectionHandler,并且一旦函数结束,实例就被销毁了。关于 thislink,我发现我必须创建一个指针,该指针指向使用“new”运算符创建的处理程序 (ConnectionHandler* newHandler = new ConnectionHandler;)。在这种情况下(正如我所理解的)指针被破坏,但 ConnectionHandler 的实例保留在内存中。再次感谢您的帮助!
  • @sh4kesbeer:这仍然可能是错误的解决方案。您需要考虑哪个线程“拥有” ConnectionManager 对象。在这种情况下,唯一真正的选择是分离线程,因为该线程需要对象,只要它存在,您就无法关闭它。这意味着您需要有一种方法来“要求”分离的线程来销毁您的对象并自行关闭。使用new 创建它有点工作,但它会造成内存泄漏,并且总体上是一个混乱的解决方案。
  • 我不明白你的意思,在实际项目的上下文中(不是这个最小的解决方案),ConnectionHandler 由处理传入连接和断开连接的接受器(对象)创建和拥有。计划(目前)是这个 Acceptor 将根据需要动态销毁和创建 ConnectionHandler。我希望这是可行的,因为数据可以在进程范围内访问并且不受线程的限制。
  • @sh4kesbeer:如果ConnectionHandler 仍在创建一个分离线程,您仍然需要一种方法来通知分离线程它应该关闭,然后joinConnectionHandler 中使用它析构函数。
猜你喜欢
  • 2015-09-26
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 2017-11-21
  • 2022-07-31
  • 1970-01-01
  • 2010-11-22
相关资源
最近更新 更多