【问题标题】:Lambda capturing this and long-running functionLambda 捕获这个和长时间运行的函数
【发布时间】:2017-07-14 16:41:54
【问题描述】:

我有一个需要很长时间才能运行的函数,但幸运的是它是异步运行的。我想获取此函数调用的结果并将其设置为类实例的私有变量。看起来很简单:

// Exists in some other library.
void LongRunningAsync(std::function<void(int)> callback) {
  sleep(10);
  callback(5);
}

class A {
 public:
  void Do() {
    auto lambda = [this](int val) {
      // Some processing...
      var_ = val;
    };
    LongRunningAsync(lambda);
  }

 private:
  var_;
};

int main() {
  A* a = new A;
  a->Do();
  // Wait for LongRunningAsync to finish.
  sleep(20);
  return 0;
}

问题在于将以下行添加到main,就在评论之前:

delete a;

现在当LongRunningAsync 调用回调时,它将尝试修改已删除实例(即UB)的成员变量。

有没有办法挽救这种方法?我最近了解到以下解决方案:

void LongRunningAsync(std::function<void(int)> callback) {
  sleep(10);
  callback(5);
}

class A : public std::enable_shared_from_this<A> {
 public:
  void Do() {
    std::weak_ptr<A> weak = shared_from_this();
    auto lambda = [weak](int val) {
      auto shared = weak.lock();
      if (!shared) return;
      // Some processing...
      shared->var_ = val;
    };
    LongRunningAsync(lambda);
  }

 private:
  var_;
};

int main() {
  auto a = std::make_shared<A>();
  a->Do();
  // Wait for LongRunningAsync to finish.
  sleep(20);
  return 0;
}

但这需要将 all A 变量更改为 shared_ptr。有没有一种侵入性较小的方法来完成这项工作?

【问题讨论】:

  • 也许您可以让A 类拥有一个包含所有所需状态的shared_ptr
  • 您不应与 sleep 同步,而应与 thread::join 或类似的同步。
  • 嗯,首先,你真的必须使用那个库吗?这个“长时间运行的异步函数”的界面对我来说看起来很笨拙。我希望异步函数返回 std::future 并让客户端决定何时获取结果而不是调用回调。其次,我假设在您的示例中 sleep(10); callback(5); 应该在单独的线程中调用,因为目前对 LongRunningAsync 的调用实际上会阻止执行。
  • @Curious:这实际上是一个非常好的主意!让我试试……
  • @Jarod42:这只是一个例子。

标签: c++ shared-ptr race-condition undefined-behavior weak-ptr


【解决方案1】:

一种可能的解决方案是将您需要的状态封装到shared_ptr 成员变量中,然后按值将其捕获到异步运行的闭包中。

类似下面的东西

class A : public std::enable_shared_from_this<A> {
public:
  void Do() {
    auto lambda = [member_shared_state](int val) {
      member_shared_state->var_ = val;
    };
    LongRunningAsync(lambda);
  }
  ....
};

【讨论】:

    【解决方案2】:

    这是一个基于 Curious 方法的解决方案,但这并不强迫我将所有指向 A 对象的指针更改为 shared_ptr

    // Exists in some other library.
    void LongRunningAsync(std::function<void(int)> callback) {
      sleep(10);
      callback(5);
    }
    
    class A {
     public:
      A() : var_(std::make_shared<int>()) {}
      void Do() {
        std::weak_ptr<int> weak = var_;
        auto lambda = [weak](int val) {
          auto shared = weak.lock();
          if (!shared) {
            return;
          }
          // Some processing...
          *shared = val;
        };
        LongRunningAsync(lambda);
      }
    
     private:
      std::shared_ptr<int> var_;
    };
    
    int main() {
      A* a = new A;
      a->Do();
      delete a;
      // Wait for LongRunningAsync to finish.
      sleep(20);
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-01-26
      • 1970-01-01
      • 2012-11-07
      • 1970-01-01
      • 2021-06-28
      • 2017-10-05
      • 2023-01-10
      • 2016-09-22
      • 2020-08-19
      相关资源
      最近更新 更多