【问题标题】:Concurrency: Using pthread_mutex to atomize an increment operation并发:使用 pthread_mutex 原子化增量操作
【发布时间】: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(&amp;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


【解决方案1】:

您不应该将互斥锁作为mythread 中的局部变量,因为每个线程都会获得它自己的副本。将其设为全局并在 main 中对其进行初始化。

【讨论】:

  • 非常感谢,它现在可以工作了,我明白为什么以前没有。一个问题,如果我给每个线程一个锁,从而使互斥锁无用,为什么它比没有它花费的时间要长得多?
  • @DTek 我想它仍然必须在每次循环迭代时“锁定”每个线程。
猜你喜欢
  • 2015-10-15
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 2014-01-11
  • 1970-01-01
  • 2018-01-06
  • 1970-01-01
  • 2017-08-24
相关资源
最近更新 更多