我能否使 C++11 类/对象通用线程安全?

Can I make a C++11 class/object universally thread safe?

本文关键字:线程 安全 对象 C++11      更新时间:2023-10-16

阅读了互斥实现可以互换(独立于线程实现)后,我的理解是,通过使用C++ 11 个互斥体,我可以确保我的类对所有线程实现都是线程安全的。

我的理解正确吗?我期待是(也许有警告)或否(有理由)。

  • 我的代码可能需要在目标平台上重新编译,但不需要对我的源代码进行任何更改。(@Peter)
  • 或者说,我应该能够针对特定平台进行编译并提供一个可以安全地与任何线程库一起使用的 .?

鉴于反对票,我将尽量更准确。

互斥锁的 C++11 实现是否独立于任何其他线程 API?C++11 互斥锁是否只知道彼此并独立于执行线程如何到达该点而实现阻塞。

C++11 互斥锁是否始终适用于 OpenMP(带重新编译)?团队 A 能否提供线程安全 API(使用 C++ 互斥体)供团队 B 使用,而不管团队 B 使用的特定线程 API?所有代码都可以重新编译。

我附上一个工作示例。

#include <iostream>
#include <omp.h>
#include <mutex>
class A {
unsigned n = 0;
public:
void inc() {
++n;
}
unsigned get() {
return n;
}
};
class B {
std::mutex mutex;
unsigned n = 0;
public:
void inc() {
mutex.lock();
++n;
mutex.unlock();
}
unsigned get() {
return n;
}
};
int main() {
A a;
B b;
#pragma omp parallel for
for (int i = 0; i < 100000; i++) {
a.inc();
b.inc();
}
std::cout << a.get() << " " << b.get() << std::endl;
}

我上次运行的输出是:

98015 100000

这是运气还是设计?

如果将所有成员设为私有,并且对受任何同步机制(std::mutexstd::atomic或其他受 boost/Qt/其他保护的数据进行所有访问(读/写)操作,则无论使用何种线程 API,都可以使类独立于线程平台。

最简单的方法是对类内的所有数据访问操作(getter/setter)使用单个std::recursive_mutex/std::unique_lock对,并使数据成员私有。无论线程 API 如何,它都可以保证工作,但可能不是最佳的性能。

大多数其他解决方案可能适合或不适合您的具体情况。有时根本不需要同步原语。

您始终可以使用互斥体使类的成员函数在没有数据争用的情况下进行交互。但这还不足以确保可以从多个线程使用一个对象而不会发生冲突;您需要更广泛的设计。例如,考虑写出向量的内容:

int size = vec.size();
for (int i = 0; i < size; ++i)
std::cout << vec[i] << ' ';
std::cout << 'n';

使用内部互斥锁,对vec.size()的调用和对vec::operator[]的调用没有数据争用,因此从多个线程调用它们是"安全的"。但是这段代码有一个问题:如果另一个线程从向量中删除一个元素,那么向量将比原来小,并且这个循环将从末尾运行。