Lambda捕获这个和长期运行的功能

时间:2017-07-14 16:41:55

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

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

// 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;
}

但它需要将所有 A变量更改为shared_ptr。是否有一种较少侵入性的方法来使这项工作?

2 个答案:

答案 0 :(得分: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);
  }
  ....
};

答案 1 :(得分:1)

这是基于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;
}