【发布时间】:2019-03-26 18:56:34
【问题描述】:
我目前正在阅读操作系统:三个简单的部分,我开始了解并发背后的逻辑。在第 26 章中,我们得到了这个线程示例以及围绕原子性的问题:
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
static volatile int counter = 0;
// mythread()
// Simply adds 1 to counter repeatedly, in a loop
// No, this is not how you would add 10,000,000 to
// a counter, but it shows the problem nicely.
void *mythread(void *arg){
printf("%s: begin\n", (char *) arg);
int i;
for (i = 0; i < 1e7; i++) {
counter = counter + 1;
}
printf("%s: done\n", (char *) arg);
return NULL;
}
// main()
// Just launches two threads (pthread_create)
// and then waits for them (pthread_join)
int main(int argc, char *argv[]) {
pthread_t p1, p2;
printf("main: begin (counter = %d)\n", counter);
pthread_create(&p1, NULL, mythread, "A");
pthread_create(&p2, NULL, mythread, "B");
// join waits for the threads to finish
pthread_join(p1, NULL);
pthread_join(p2, NULL);
printf("main: done with both (counter = %d)\n", counter);
return 0;
}
它向我们展示了问题在于,由于增量中的竞争条件,总和的值会发生变化,并且很少是假设的。
例如,编译后:
gcc -g -o main page6.c -Wall -pthread
我跑了两次:
main: begin (counter = 0)
A: begin
B: begin
A: done
B: done
main: done with both (counter = 10263001)
和:
main: begin (counter = 0)
A: begin
B: begin
A: done
B: done
main: done with both (counter = 10600399)
所以在阅读了互斥锁后,我尝试对 mythread() 函数中的代码进行一些小改动。
void *mythread(void *arg){
pthread_mutex_t lock;
int rc = pthread_mutex_init(&lock,NULL);
assert(rc==0); //always check sucess
printf("%s: begin\n", (char *) arg);
int i;
for (i = 0; i < 1e7; i++) {
pthread_mutex_lock(&lock);
counter = counter + 1;
pthread_mutex_unlock(&lock);
}
printf("%s: done\n", (char *) arg);
return NULL;
}
在编译(以相同的方式)并运行之后,它确实需要更多的时间(1-2 秒)。
但结果也好不到哪里去:
main: begin (counter = 0)
A: begin
B: begin
B: done
A: done
main: done with both (counter = 10019830)
和:
main: begin (counter = 0)
A: begin
B: begin
B: done
A: done
main: done with both (counter = 10008806)
那么为什么这不起作用呢?这不应该在代码中创建一个关键部分吗?我错过了什么明显的东西吗?
【问题讨论】:
-
这些语句:
pthread_mutex_t lock; int rc = pthread_mutex_init(&lock,NULL);应该在main()中,而不是线程函数中。 -
关于:
gcc -g -o main page6.c -Wall -pthread最好写成:gcc -g -o main page6.c -Wall -Wextra -Wconversion -pedantic -std=gnu11 -pthread -
@user3629249:你“忘记”了
-Werror——这让其他警告选项有了新的意义,因为如果有任何警告,代码将无法成功编译。
标签: c concurrency pthreads mutex