使用int to void * cast避免竞争条件

时间:2017-03-19 13:45:57

标签: c pthreads posix

我想了解下面的代码。这段代码没有竞争条件,但我无法理解。

#include <stdio.h>
#include <pthread.h>

void *foo(void *vargp) {
    int id;
    id = (int)vargp;
    printf("Thread %d\n", id);
}

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

    for (i = 0; i < 2; i++)
        pthread_create(&tid[i], NULL, foo, (void *)i);
    pthread_join(tid[0], NULL);
    pthread_join(tid[1], NULL);
    return 0;
}

intvoid*的类型转换是如何工作的?

1 个答案:

答案 0 :(得分:1)

此代码说明了避免的竞争条件,这与原始代码非常相似,但略有不同,差异使其不正确:

/* Incorrect code! */
#include <pthread.h>
#include <stdio.h>

static void *foo(void *vargp)
{
    int id = *(int *)vargp;
    printf("Thread %d\n", id);
    return 0;
}

int main(void)
{
    pthread_t tid[2];
    int i;

    for (i = 0; i < 2; i++)
        pthread_create(&tid[i], NULL, foo, &i);  // Bad idea!
    pthread_join(tid[0], NULL);
    pthread_join(tid[1], NULL);
    return 0;
}

由于函数foo采用void *参数,因此将int的地址传递给它是合乎逻辑的。但是,这有一个主要问题:

  • 无法保证线程执行的顺序,也不确定何时执行,因此无法知道线程将看到哪些值。

实际上,当我第一次运行此代码时,两个线程都报告了2

解决这个问题的方法是不传递i的地址,而是按值传递i。但是,参数仍然应该是void *,因此代码在调用i之前将void *强制转换为pthread_create(),并且线程函数撤消强制转换以检索值。

当我这样做时,我也使用<stdint.h>使uintptr_t类型可用,我使用:

int id = (uintptr_t)vargp;

pthread_create(&tid[i], NULL, foo, (void *)(uintptr_t)i);

看起来过分和/或过于强迫,但uintptr_t强制转换确保整数与指针的大小相同,以避免'cast to pointer from integer of different size'编译器警告(因为我告诉编译器将所有警告视为错误,对我来说是必要的,以便完全编译代码。

如果确实将指向数据的指针传递给线程函数(本讨论中为foo),则必须确保您创建的每个线程都获得自己的数据副本,除非该数据是每个帖子都相同。

您可以在POSIX threads — unique execution中看到此技术。