min*_*ief 4 multithreading pthreads
假设我有两个线程A和B都递增一个~global~variable"count".每个线程像这样运行一个for循环:
for(int i=0; i<1000; i++)
count++; //alternatively, count = count + 1;
Run Code Online (Sandbox Code Playgroud)
即每个线程增量计数1000次,并且假设计数从0开始.在这种情况下是否会出现同步问题?或者在执行完成后计数正确等于2000?我想因为语句"count = count + 1"可能会分解成两个汇编指令,所以在这两个指令之间有可能交换另一个线程吗?不确定.你怎么看?
blu*_*ucz 11
是的,在这种情况下可能存在同步问题.您需要使用互斥锁保护count变量,或使用(通常是特定于平台的)原子操作.
使用pthread互斥锁的示例
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
for(int i=0; i<1000; i++) {
pthread_mutex_lock(&mutex);
count++;
pthread_mutex_unlock(&mutex);
}
Run Code Online (Sandbox Code Playgroud)
使用原子操作
这里有一个关于平台特定原子操作的事先讨论: UNIX便携式原子操作
如果您只需要支持GCC,这种方法很简单.如果您支持其他编译器,您可能必须做出每个平台的决策.
是的,可能存在同步问题。
作为可能问题的一个示例,不能保证增量本身是原子操作。
换句话说,如果一个线程读取增量值然后被换出,另一个线程可以进来并更改它,那么第一个线程将写回错误的值:
+-----+
| 0 | Value stored in memory (0).
+-----+
| 0 | Thread 1 reads value into register (r1 = 0).
+-----+
| 0 | Thread 2 reads value into register (r2 = 0).
+-----+
| 1 | Thread 2 increments r2 and writes back.
+-----+
| 1 | Thread 1 increments r1 and writes back.
+-----+
Run Code Online (Sandbox Code Playgroud)
因此您可以看到,即使两个线程都尝试增加该值,它也只增加了 1。
这只是可能出现的问题之一。也可能是写入本身不是原子的,并且一个线程在被换出之前可能仅更新部分值。
如果您有保证在您的实现中起作用的原子操作,则可以使用它们。否则,请使用互斥锁,这就是 pthreads 提供的同步功能(并且保证有效),因此是最安全的方法。
计数显然需要通过互斥体或其他同步机制来保护。
从根本上来说,count++ 语句可分解为:
load count into register
increment register
store count from register
Run Code Online (Sandbox Code Playgroud)
上下文切换可能发生在任何这些步骤之前/之后,从而导致以下情况:
Thread 1: load count into register A (value = 0)
Thread 2: load count into register B (value = 0)
Thread 1: increment register A (value = 1)
Thread 1: store count from register A (value = 1)
Thread 2: increment register B (value = 1)
Thread 2: store count from register B (value = 1)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,两个线程都完成了一次循环迭代,但最终结果是 count 只增加了一次。
您可能还希望使计数变得易失,以强制加载和存储到内存中,因为一个好的优化器可能会将计数保留在寄存器中,除非另有说明。
另外,我建议,如果这就是您的线程中要完成的所有工作,那么保持一致所需的所有互斥锁定/解锁的性能将急剧下降。线程应该有更大的工作单元来执行。
| 归档时间: |
|
| 查看次数: |
7546 次 |
| 最近记录: |