为什么我创建的线程没有按顺序打印?

时间:2018-11-26 23:43:41

标签: c multithreading pthreads race-condition

我有这个程序:

void *func(void *arg) {
    pthread_mutex_lock(&mutex);
    int *id = (int *)arg;

    printf("My ID is %d\n" , *id);
    pthread_mutex_unlock(&mutex);
}

int main() {
    int i;
    pthread_t tid[3];

    // Let us create three threads
    for (i = 0; i < 3; i++) {
        pthread_create(&tid[i], NULL, func, (void *)&i);
    }

    for (i = 0; i < 3; i++) {
        pthread_join(tid[i], NULL);
    }

    pthread_exit(NULL);
    return 0;
}

我希望它输出以下内容:

My ID is 0
My ID is 1
My ID is 2

但是我得到的是随机输出,例如:

My ID is 0
My ID is 0
My ID is 2

由于我已经添加了互斥锁,所以我认为它可以解决问题。我还做错了什么?这与种族状况有关吗?

2 个答案:

答案 0 :(得分:4)

这里id指向所有线程main中的相同变量i

int *id = (int *)arg;

printf("My ID is %d\n" , *id);

但是变量i不断地被线程后面的for中的两个main循环更新。因此,在线程到达printf的点之前,i的值以及*id的值可能已经更改。

有几种解决方法。最好的方法取决于用例:

  1. 在修改main或使其超出范围之前,请等待*id直到线程发出信号,表明线程已复制了i
  2. 声明并初始化数组int thread_id[],并创建如下线程: pthread_create(&tid[i], NULL, func, &thread_id[i]);
  3. malloc的一些内存,并使用i的副本对其进行初始化:

    int *thread_id = malloc(sizeof(*thread_id));
    *thread_id = i
    pthread_create(&tid[i], NULL, func, thread_id);
    

    请别忘了在线程使用完毕后将free的内存存储在线程中。或在main中,如果线程无法启动。

  4. 如果i适合void *,则可以将其内容直接作为参数传递给线程。为确保适合,您可以将其声明为intptr_t而不是int (我们从根本上滥用了指针不过是 magic 整数的事实):

    void *func(void *arg) {
        pthread_mutex_lock(&mutex);
        // Here we interpret a pointer value as an integer value
        intptr_t id = (intptr_t )arg;
    
        printf("My ID is %d\n" , (int)id);
        pthread_mutex_unlock(&mutex);
    }
    
    int main() {
        intptr_t i;
        pthread_t tid[3];
    
        // Let us create three threads
        for (i = 0; i < 3; i++) {
            // Here we squeeze the integer value of `i` into something that is
            // supposed to hold a pointer
            pthread_create(&tid[i], NULL, func, (void *)i);
        }
    
        for (i = 0; i < 3; i++) {
            pthread_join(tid[i], NULL);
        }
    
        // This does not belong here !!
        // pthread_exit(NULL);
        return 0;
    }
    

答案 1 :(得分:1)

不,不涉及竞争条件。(我的b)i上可能存在竞争条件,因为所有线程都可以访问它。每个线程都以指向i的指针开始。但是,主要问题是,不能保证线程在我按您期望的顺序保存您期望的值的同时启动并运行关键部分。

我假设您全局声明了变量mutex,并在某个地方调用了pthread_mutex_init()对其进行了初始化。

Mutexes非常有用,它允许一次仅一个线程一次访问代码的关键部分。因此,您编写的代码会创建所有三个线程以并行运行,但一次只能让一个线程运行以下代码。

int *id = (int *)arg;

printf("My ID is %d\n" , *id);