【问题标题】:How to deal with shared data when throwing exceptions抛出异常时如何处理共享数据
【发布时间】:2013-07-26 05:01:53
【问题描述】:

当有多个线程使用共享数据时,当抛出异常时如何正确处理该数据的销毁?

我正在开发一个应用程序,我希望一个进程在等待通过网络从另一个进程发送的结果时工作。我实现了一个类,它创建一个线程来等待结果,如下所示(实际代码太大,此处无法发布,所以这是一个小例子):

class Notifier {
  public: 
    Notifier(Client* _client) : value(-1), client(_client) {
      listener = boost::thread(&Notifer::Listen, this);
    }

    void Listen() {
      try {
        int rec = client->Receive(); //blocking call to receive data over a socket
        boost::scoped_lock sl(mutex);
        value = rec;
      } catch (...) {
        cout << "Exception thrown in listener" << endl;
      }
    }

    int Get() {
      boost::mutex::scoped_lock sl(mutex);
      return value;
    }

  private:
    int value;
    boost::mutex; 
    boost::thread listener;
    Client* client;
}

int main() {
  Client client; //initialize client, connect, etc.
  Notifier n(client);
  while(n.Get() < 0) {
   // do work
   cout << "Waiting for data" << endl;
   sleep(1);
  }
}

这对我来说很好,直到我添加异常处理:

int main() {
  try {
    Client client; //initialize client, connect, etc.
    Notifier n(client);
    while(n.Get() < 0) {
     // do work
     cout << "Waiting for data" << endl;
     sleep(1);
     throw exception();
    }
  } catch(...) {
    cout << "Exception thrown in main" << endl;
  }
  return 0;
}

我得到了错误

“增强:pthread_mutex_lock 中的互斥锁失败:参数无效”。

我的猜测是,当异常被抛出时,main 函数的堆栈会被展开,从而破坏互斥体。然后Listen函数中的Receive()调用返回,scoped_lock构造函数尝试锁定不存在的互斥体,导致错误。

有人可以确认这确实是正在发生的事情吗?如果是这样,有没有办法与线程通信互斥锁不再存在或线程应该终止?还是有一种更安全的方式来做我想做的事情?

【问题讨论】:

    标签: c++ multithreading boost exception-handling local-variables


    【解决方案1】:

    您必须为Notifier 类编写一个正确的析构函数,这将取消所有阻塞的调用(即解除阻塞client-&gt;Receive())然后终止listener 线程。此外,您需要修改Notifier::Listen() 以定期检查线程终止请求...然后一切都会好的。

    【讨论】:

    • 这是一个有趣的想法,只要我可以真正取消被阻止的呼叫。我的Client类使用了Unix的recv函数,你知道如何解除阻塞吗?
    • 找到了 - 有一个 shutdown 函数。谢谢!我不知道这是可能的。
    • 我还有一个问题。如果我按照您的建议使用通知程序的析构函数,我会做例如client-&gt;Shutdown()。但是这个客户端并不是真正由通知程序“拥有”的,它是作为参数传入的,在更复杂的程序中,它可以在通知程序超出范围后在其他地方使用。在这种情况下,我不想关闭客户端。有没有办法区分正常破坏和异常造成的破坏,所以我只在第二种情况下关闭客户端?
    • 我不明白你的应用程序的精确设计(包括一个“复杂”的应用程序),但我想知道:0)你为什么不使用 boost::asio; 1)这个设计对我来说是另一个“问题”:你有Notifier,它与Client紧密耦合; 2) 为什么不使用带有工作池的完全异步 I/O?
    • 客户端实际上是以前程序员的遗留代码,我仍在逐步淘汰。几天后我有一个截止日期,在那之前我无法完成,所以我只是在尝试使用我现在拥有的东西。我同意客户端和通知程序不应该紧密耦合。您能否详细说明一下工人池?
    猜你喜欢
    • 2013-07-24
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    • 2019-03-20
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 2011-02-15
    相关资源
    最近更新 更多