【问题标题】:Call a function before function exits在函数退出之前调用函数
【发布时间】:2015-10-09 06:27:46
【问题描述】:

我将从一个例子开始。假设我需要使用互斥锁中的函数来保护代码。有两种实现方式。

#include <iostream>
#include <vector>
#include <pthread.h>



pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
std::vector<float> myVec;

void threadfunc(int i, float value)
{
  pthread_mutex_lock(&myMutex);
  if(i <= 0 || i > myVec.size())
  {
    pthread_mutex_unlock(&myMutex);
    return;
  }

  if(value < 0)
  {
    pthread_mutex_unlock(&myMutex);
    return;
  }

  myVec[i] += value;
  pthread_mutex_unlock(&myMutex);
  return;
}

class AUTOMUTEX
{
  private:
    pthread_mutex_t *mMutex;
  public:
    AUTOMUTEX(pthread_mutex_t *mutex): mMutex(mutex)
  {
    pthread_mutex_lock(mMutex);
  }

    ~AUTOMUTEX()
    {
      pthread_mutex_unlock(mMutex);
    }
};


void threadfunc_autolock(int i, float value)
{
  AUTOMUTEX  autoMutex(&myMutex);
  if(i <= 0 || i > myVec.size())
  {
    return;
  }

  if(value < 0)
  {
    return;
  }

  myVec[i] += value;
  return;
}

int  main()
{
  threadfunc_autolock(5, 10);
  threadfunc(0, 7);
  return 1;
}

从示例中可以清楚地看出,threadfunc autolock 是更好的实现,因为调用 pthread_mutex_unlock 函数返回由对 AUTOMUTEX 的析构函数调用来处理(C++ 11 线程支持这一点。因此,如果我们不需要我们自己的 AUTOMUTEX 实现正在使用 C++11 线程库)。 每次我们需要使用一些设置/重置函数对来执行此操作时,有没有一种方法可以在不实现包装类的情况下实现这一点。 boost 或 C++ 11 是否有一些预定义的模板类,我们可以使用它为任何此类“设置/重置”类型的函数实现 AUTOMUTEX 的行为。这对于具有多个返回点的函数非常有用。 换句话说,boost/C++ 是否提供了具有以下行为的类。

//sample code not compilable.
template <class T, class Y>
class myAuto
{
  myAuto()
{ 
   T();
}
  ~myAuto()
{
  Y();
};

【问题讨论】:

  • 您是专门寻找互斥锁/解锁机制,还是在进入和退出时对两个不相关函数的通用调用?
  • Boost thread library 已经有很长时间的作用域锁了。

标签: c++ c++11 boost


【解决方案1】:

您可以编写自己的通用 RAII 类,例如:

class Finally
{
public:
    explicit Finally(std::function<void()> f) : mF(f) {}
    ~Finally() noexcept() {
        try
        {
            mF();
        } catch (...) {
            // Handle error.
        } 
    }

    Finally(const Finally&) = delete;
    Finally(Finally&&) = delete;

    Finally& operator=(const Finally&) = delete;
    Finally& operator=(Finally&&) = delete;

private:
    std::function<void()> mF;
};

用法:

{
    pthread_mutex_lock(&myMutex);
    Finally finally([&](){ pthread_mutex_unlock(&myMutex); });

    //..
}

即使专用 RAII 对象在某些情况下可能更合适(作为互斥体)。

【讨论】:

    【解决方案2】:

    有人提议在下一个 C++ 标准中包含通用范围保护,我认为它已被接受。您可以找到实现 here,以及参考论文的链接。

    原则上,它类似于经典的 ScopeGuard,但它也提供了一些特殊情况,例如用于类 C 文件 API。

    【讨论】:

      【解决方案3】:

      您可以使用ScopeGuard 之类的内容。 (现在有点过时了。)

      但是考虑到为每种资源类型构建一个特定的 RAII 包装器是多么容易和清晰,我通常会这样做。

      (我认为 boost 从来没有采用过像 ScopeGuard 这样的东西。使用std::function、lambdas 等很容易自己做。)

      【讨论】:

      【解决方案4】:

      编写自己的通用资源包装器有什么问题?

      template <typename Res, typename Fn = std::function<void(Res*)>>
      class resource_mgr
      {
          Res* resource;
          Fn initialize, finalize;
      
      public:
          resource_mgr (Res* r, Fn i, Fn f)
          : resource(r),
            initialize(i),
            finalize(f)
          {
              initialize(resource);
          }
      
          resource_mgr (resource_mgr const&) = delete;
          resource_mgr (resource_mgr&&)      = delete;
      
          resource_mgr const& operator = (resource_mgr const&) = delete;
          resource_mgr const& operator = (resource_mgr&&)      = delete;
      
          ~resource_mgr
          {
              try
              {
                  finalize(resource);
              }
              catch(...)
              {
                  std::cerr << "Uh-oh!"
              }
          }
      };
      

      你可以让它保持简单,也可以像这样疯狂——使用智能指针、定义移动操作、添加对自定义错误处理程序的支持等。你可以像这样使用它:

      void threadfunc_autolock(int i, float value)
      {
          resource_mgr<mutex_t>  autoMutex (
              &myMutex,
              [](auto* p) { if (!pthread_mutex_lock(p)) throw Something(); },
              [](auto* p) { if (!pthread_mutex_unlock(p)) throw Something(); }
          );
      
          /* . . . */
      }
      

      【讨论】:

        【解决方案5】:

        这是一个使用 Boost.ScopeExit 的示例(未经测试):

        #include <boost/scope_exit.hpp>
        
        ...
        
        void threadfunc_autolock(int i, float value)
        {
            pthread_mutex_lock(&myMutex);
        
            BOOST_SCOPE_EXIT(&myMutex) {
                pthread_mutex_unlock(&myMutex);
            } BOOST_SCOPE_EXIT_END
        
            if(i <= 0 || i > myVec.size())
            {
                return;
            }
        
            if(value < 0)
            {
                return;
            }
        
            myVec[i] += value;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多