类似于嵌套互斥锁,但更通用

Something like a nested mutex but more generic?

本文关键字:嵌套 类似于      更新时间:2023-10-16

我正在研究一个项目,其中必须在对类的成员对象执行某些操作后保存文件。有时我们希望在一次操作后保存文件,有时我们需要在执行了一批操作后才保存文件。

我的想法是使用一个类,它基本上像递归互斥锁一样工作。除了不锁定和解锁互斥锁外,我希望当类在堆栈中的最后一个实例超出作用域时,类调用一个方法(在本例中是保存文件)。

实现这样做的类不是问题,但这感觉像是一个通用问题,我只是在Boost或STL中找不到。对于这个问题是否存在预先存在的标准解决方案,或者我是否需要滚动我自己的类来完成它?如果是这样,我的方法是正确的,还是有更好的方法来解决问题?

下面是我正在寻找的行为的简单实现。即使DoItOnce()被调用了11次,它也只会打印两次"Hello World!"我想使用像GenericGuard这样的东西,通过从公认的标准中提取它,而不是在代码库中粘贴我自己的实现。这可能吗?

#include <iostream>
void Noop (void) { }
void HelloWorld (void) { std::cout << "Hello World!" << std::endl; }
// This is what I imagine a generic implementation would look like...
template <void (*InitFunc)(), void (*DestructFunc)()>
class GenericGuard
{
  int & _i;
  public:
  GenericGuard (int & i) : _i(i) { if (_i++ == 0) { InitFunc(); } }
  ~GenericGuard () { if (--_i == 0) { DestructFunc(); } }
};
int HelloWorldCounter; // Use a factory class in real-world?
typedef GenericGuard<Noop, HelloWorld> HelloWorldGuard;
void DoSomethingOnce (void) 
{
  HelloWorldGuard G (HelloWorldCounter);
  // Do something
}
void DoItTenTimes (void)
{
  HelloWorldGuard G (HelloWorldCounter);
  for (int i = 0; i < 10; ++i)
  {
    DoSomethingOnce();
  }
}
int main (void)
{
  DoSomethingOnce();
  DoItTenTimes();
  return 0;
}

您可以使用带有自定义删除函数的shared_ptr

  • STL (c++11起):http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
  • 增加:http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/shared_ptr.htm deleter_constructor

的例子:

#include <memory>
#include <iostream>
void    HelloWorld(void *) { std::cout << "Hello World!" << std::endl; }
class   factory
{
public:
  static std::shared_ptr<void> get_instance()
  {
    static std::weak_ptr<void>   ref;
    if (ref.expired())
      {
        std::shared_ptr<void>    sp{nullptr, HelloWorld};
        ref = sp;
        return sp;
      }
    return ref.lock();
  }
};
void DoSomethingOnce (void)
{
  std::shared_ptr<void>  G = factory::get_instance();
  // Do something
}
void DoItTenTimes (void)
{
  std::shared_ptr<void>  G = factory::get_instance();
  for (int i = 0; i < 10; ++i)
    {
      DoSomethingOnce();
    }
}
int     main(void)
{
  DoSomethingOnce();
  DoItTenTimes();
  return 0;
}

您所追求的模式似乎是众所周知的:您正在寻找事务中的组操作[1]

我想到的相关模式是

  • 命令模式(带有著名的Do/Undo示例)

    • 复合模式(所以你可以有一个命令,组成几个其他命令,形成一个树);
  • 工作单位模式;这可以让你将未决编辑分组,并将它们应用为一个组

  • 软件事务性内存(关注操作的真正原子性,例如异常安全)

不,我不是模式的坚定倡导者,但我喜欢它给你谈论事物的概念:所以,你真正想要的是工作单元(它可以像分组命令一样简单),你想要一个"事务",在破坏时自动应用更改。


根据您的实际应用程序,坚持使用您现在使用的可变对象方法,只是偶尔序列化它可能是好的。如果应用程序领域变得稍微有趣一些(例如,使用线程、撤销和/或版本控制?),您很快就会发现,当您转移到文档模型(对不可变节点的引用图)时,生活会变得更加简单。这允许您通过简单地替换节点来廉价地"编辑"复杂的对象图。节点是不可变的,即使在线程环境中,共享它们也是安全的。

认为与你的问题有关的是Sean Parent的调味c++ 谈话。虽然这篇文章关注的是如何组织文档模型,但我觉得它可能非常有见地,可能会给您带来观点上的"突破性"变化,使问题再次变得简单。


[1]在这个上下文中根本不需要是原子的,尽管它们可能(需要)在您的应用程序域中