在 C++ 线程中,我应该通过值还是引用传递 shared_ptr?

Pla*_*ove 7 c++

微软的线程安全页面说,shared_ptr即使有多个副本共享同一个对象,也应该使用它。

那么这是否意味着以下两种情况都可以接受?我已经尝试了两者,它们似乎工作正常。

编辑:实际的业务目标是从长时间运行的线程到主线程获取字符串更新。我想我应该使用,shared_ptr因为string它不是线程安全的。不要诚实地关心所有权。

选项 1(传递参考):

auto status = std::make_shared<std::string>();
auto f = [&status]() {
    ...
  *status = "current status";
    ...
};

std::thread t{f};

while(true) {
  std::cout << *status << std::endl;
  std::this_thread::sleep_for(1000ms);
  if (*status == "completed") break;
}

t.join();
Run Code Online (Sandbox Code Playgroud)

选项 2(制作副本):

auto status = std::make_shared<std::string>();
auto f = [](std::shared_ptr<std::string> s) {
    ...
  *s= "current status";
    ...
};

std::thread t{f, status};

while(true) {
  std::cout << *status << std::endl;
  std::this_thread::sleep_for(1000ms);
  if (*status == "completed") break;
}

t.join();
Run Code Online (Sandbox Code Playgroud)

EDIT2:所以显然这两种方法对于我想要实现的目标都是错误的。我需要使用std::mutex( cppreference ) 而不是shared_ptr. 请参阅此答案的后半部分。

eer*_*ika 11

通常,线程可能会在创建它们的范围内存活。在这种情况下,任何通过引用捕获的局部变量都可能在线程仍在运行时被销毁。如果是这种情况,则不应通过引用捕获。

此外,在一个线程中修改共享指针对象并在没有同步的情况下访问另一个线程会导致未定义的行为。如果这就是您所做的,那么您应该使用std::atomic_load/atomic_store函数访问指针,或者简单地将指针复制到每个线程中。请注意,您可以通过副本捕获:

auto f = [status]() {
Run Code Online (Sandbox Code Playgroud)

此外,共享指针没有提供额外的线程安全来访问指向的对象,除了保持所有权处于活动状态并确保它被删除一次。如果指向的类型不是原子的,那么在一个线程中修改它并在没有同步的情况下访问另一个线程会导致未定义的行为。如果这就是你正在做的,你需要使用互斥锁或类似的东西。或者将指向的对象本身复制到每个线程中。

关于编辑后的问题:您的示例适用于最后一个案例。他们都有未定义的行为。你需要同步。

  • @AdrianMaire Standard 这样说:“如果内存位置([intro.memory])上的副作用相对于同一内存位置上的另一个副作用或使用同一内存位置中任何对象的值进行的值计算是无序的,并且它们不是潜在并发的([intro.multithread]),行为未定义。简而言之,*数据竞争*是 UB。 (3认同)

ALX*_*23z 7

shared_ptr通过引用接受是很奇怪的,因为您首先失去了使用的全部意义shared_ptr。您可以只使用原始指针。

在某些情况下,通过引用接受shared_ptr是合法的,但是如果您将它的引用提供给线程,那么一旦该实例shared_ptr被销毁并且线程仍然使用shared_ptr.

的主要目的shared_ptr是管理对象的生命周期。如果你将它的引用传递给一个线程,那么你就抛弃了shared_ptr.

  • 为什么传递一个shared_ptr作为引用(一般来说,不是说线程)会失去使用它的全部意义?显然,传递引用并删除它的对象会导致UB,但不是所有引用都是如此吗? (2认同)