我试图理解并发性,并更好地使用锁,但是我制作的这个虚拟示例使我失望:
int i = 0;
void foo() {
int n = i;
i = i + 1;
printf("foo: %d\n", n);
}
void boo() {
int n = i;
i = i + 1;
printf("boo: %d\n", n);
}
int main(int argc, char* argv[]) {
pthread_t p1, p2;
pthread_create(&p1, NULL, (void*) foo, NULL);
pthread_create(&p2, NULL, (void*) boo, NULL);
// wait for threads to finish
pthread_join(p1, NULL);
pthread_join(p2, NULL);
// final print
printf("main: %d\n", i);
return 0;
}
[如果我理解正确,i = i + 1;
和foo()
中的bar()
可能会导致某些意外行为。一种意外的行为是我们将同时获得“ foo:0”和“ bar:0”,因为有可能在i = i + 1;
之前进行上下文切换,因此n
始终为0。我认为预期的行为是“ foo:0”“ bar:1”或“ bar:0”“ foo:1”(如果我错了,请纠正我)。
为了解决这个问题,我添加了锁:
int i = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void foo() {
int n = i;
i = i + 1;
printf("foo: %d\n", n);
}
void boo() {
int n = i;
i = i + 1;
printf("boo: %d\n", n);
}
int main(int argc, char* argv[]) {
pthread_t p1, p2;
printf("Locking foo\n");
pthread_mutex_lock(&lock);
printf("Locked foo\n");
pthread_create(&p1, NULL, (void*) foo, NULL);
pthread_mutex_unlock(&lock);
printf("Unlocked foo\n");
printf("Locking boo\n");
pthread_mutex_lock(&lock);
printf("Locked boo\n");
pthread_create(&p2, NULL, (void*) boo, NULL);
pthread_mutex_unlock(&lock);
printf("Unlocked boo\n");
// wait for threads to finish
pthread_join(p1, NULL);
pthread_join(p2, NULL);
// final print
printf("main: %d\n", i);
return 0;
}
我认为这可以解决意外的结果,但是运行此命令时我得到了令人惊讶的输出:
Locking foo
Locked foo
Unlocked foo
Locking boo
Locked boo
foo: 0
Unlocked boo
boo: 1
main: 2
似乎程序锁定了第一个调用foo()的线程,然后立即将其解锁,而没有实际执行printf?然后,它继续锁定调用boo()的线程,并按顺序处理奇怪的事情。有人可以解释这种行为吗?我以为输出看起来像:
Locking foo
Locked foo
foo: 0
Unlocked foo
Locking boo
Locked boo
boo: 1
Unlocked boo
main: 2
您使用的锁不正确。您锁定互斥锁,启动线程,然后将其解锁。线程在不了解锁定操作的情况下运行。使用共享内存功能中的锁:
void foo() {
pthread_mutex_lock(&lock);
int n = i;
i = i + 1;
pthread_mutex_unlock(&lock);
printf("foo: %d\n", n);
}
与boo
功能相同。
您选择的措词背叛了可能造成的严重误会:
似乎程序锁定了调用foo()的第一个线程
程序不锁定线程。相反,线程获取锁。那也可以包括程序的主线程。在协作(!)线程之间可以实现互斥,这是因为一次只有一个线程可以持有任何特定的锁(mutex)。]
因此,如果线程B在尝试获取线程A时锁定了给定的互斥锁,则线程A的获取尝试将被阻塞(pthread_mutex_lock()
调用的返回将被延迟)。线程A在获取互斥量之前不会继续进行。因此,关键区域的边界由对同一互斥锁上的pthread_mutex_lock()
和pthread_mutex_unlock()
的调用定义。大致来说,每个参与线程必须在访问共享共享变量之前获取适当的互斥体,并且每个互斥体在完成互斥体后必须释放互斥体,以允许其他线程依次获取它。
其他答案已经提供了有关示例程序外观的详细信息。
锁定应该在此处的功能中进行:
#include <stdio.h>
#include <pthread.h>
int i = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void foo() {
printf("Locking foo\n");
pthread_mutex_lock(&lock);
printf("Locked foo\n");
int n = i;
i = i + 1;
pthread_mutex_unlock(&lock);
printf("Unlocked foo\n");
printf("foo: %d\n", n);
}
void boo() {
printf("Locking boo\n");
pthread_mutex_lock(&lock);
printf("Locked boo\n");
int n = i;
i = i + 1;
pthread_mutex_unlock(&lock);
printf("Unlocked boo\n");
printf("boo: %d\n", n);
}
int main(int argc, char* argv[]) {
pthread_t p1, p2;
pthread_create(&p1, NULL, (void*) foo, NULL);
pthread_create(&p2, NULL, (void*) boo, NULL);
// wait for threads to finish
pthread_join(p1, NULL);
pthread_join(p2, NULL);
// final print
printf("main: %d\n", i);
return 0;
}
这样,当一个功能锁定时,另一功能将被锁定,直到解锁为止。