【发布时间】:2019-07-11 16:37:34
【问题描述】:
我有一个函数可以修改我的多线程程序中的共享资源。这个函数是线程接触共享资源的唯一地方,它只用于每个线程整体工作的一小部分。
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
int64_t result = *value;
*value += to_add;
return result;
}
我想让我的应用程序线程安全,所以我在指令之间添加了一个简单的互斥锁。
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
pthread_mutex_lock(&lock);
int64_t result = *value;
*value += to_add;
pthread_mutex_unlock(&lock);
return result;
}
这样做会使我的程序慢 10 倍以上,甚至比单线程版本还要慢!
再读一读,似乎是因为 macOS 实现,它使用"fair" mutexes 而不是使用spinlocks,并且实现之间存在一定的权衡,但这种情况是其中一种情况表现不佳。但是,我这样写代码的原因是我已经在 Win32 中编写了程序(锁几乎不会造成任何性能损失),并且我也计划将该功能移植到 Linux。
有没有办法让这个函数在 macOS 中线程安全而不造成巨大的瓶颈,还是我需要重新设计平台层?
【问题讨论】:
-
您的资源在内存中的放置方式非常重要。我设法通过避免false sharing 来加速线程程序x10 - 并且没有进行任何其他更改。
alignas和 std::hardware_destructive_interference_size 在这种情况下是你的朋友。 -
AddToSharedResource(volatile int64_t* value, int64_t to_add)- 为什么指针是volatile?请注意,volatile不表示线程安全。 -
不,如果您正确同步对资源的访问,则永远不需要
volatile。易失性用于改变编译器控制之外的东西,例如直接映射到某些硬件信号的内存地址。 -
这不是
volatile的用途。它主要用于从硬件寄存器等读取。您将在此处获得的主要效果是防止编译器优化对指针的访问。你只是让你的代码运行得更慢。 -
@TedLyngmo 啊,我明白了!可以试试看!看看编译器能想出什么很有趣:D
标签: c++ multithreading macos pthreads locking