0

我在 pthreads 中制作了一个简单的程序,它通过一个结构将多个参数传递给被调用的函数。考虑这两个程序:
程序 1:

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

struct args{
    long long val1;
    int val2;
};

void *hello(void* threadid){
    struct args *tid;
    tid=(struct args*)threadid;
    printf("thread %lld\n",tid->val1);
    pthread_exit(NULL);
}

int main(){
    pthread_t threads[20];
    int i;
    for(i=0;i<20;i++){
        // ***** specific memory given to the struct *****
        struct args* a1=(struct args*)malloc(sizeof(struct args));    
        a1->val1=i;
        a1->val2=i+1;
        int ret=pthread_create(&threads[i],NULL,hello,(void*)a1);
        if(ret){
            printf("error code %d for %d\n",ret,i);
        }
    }
    pthread_exit(NULL);
}

它按预期打印输出,一些排列0..19

另一方面,考虑程序 p2

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

struct args{
    long long val1;
    int val2;
};

void *hello(void* threadid){
    struct args *tid;
    tid=(struct args*)threadid;
    printf("thread %lld\n",tid->val1);
    pthread_exit(NULL);
}

int main(){
    pthread_t threads[20];
    int i;
    for(i=0;i<20;i++){
        // ***** struct made like normal declarations *****
        struct args a1;
        a1.val1=i;
        a1.val2=i+1;
        int ret=pthread_create(&threads[i],NULL,hello,(void*)&a1);
        if(ret){
            printf("error code %d for %d\n",ret,i);
        }
    }
    pthread_exit(NULL);
}

这个程序有重复和不完整的条目,比如

thread 3
thread 5
thread 3
thread 4
thread 3
thread 6
thread 7
thread 8
thread 9
thread 10
thread 11
thread 12
thread 13
thread 15
thread 15
thread 16
thread 17
thread 18
thread 19
thread 19

为什么结构的实例化直接导致这种重叠?C不应该为循环中的每次提供一个新的内存块吗?

4

3 回答 3

3

在您的第二个示例中,在循环a1中声明了自动存储持续时间。for这意味着在每次迭代结束时,该存储位置可以重新用于下一次迭代。所以:

int ret=pthread_create(&threads[i],NULL,hello,(void*)&a1);

...您可能会将地址 ( &a1) 传递到将在后续迭代中修改的内存位置。malloc另一方面,将在每次迭代时分配一个指向不同内存位置的指针。

由于无法保证线程是否会在下一次迭代开始之前或之后执行,因此每次运行上述代码时实际上可能会得到不同的结果。

此外,malloc在不将指针存储在任何地方或在线程中释放它们的情况下使用将导致内存泄漏。在所有线程完成之前,您的结构也可能超出范围,结果是它们访问了一个悬空指针。最后,由于您没有加入任何线程,因此不知道在程序退出之前有多少线程会真正完全执行。

欢迎来到多线程的奇妙世界。

于 2013-02-12T16:12:57.873 回答
2

您的第二个示例将为循环的每次迭代获取一个新实例,a1但这很可能指向所有先前迭代使用的相同内存。

无法保证何时或多长时间会安排线程,因此您无法确定在运行时是否(以及多久)a1会更新hello。这意味着打印出的值将在 [0, 20) 范围内,但在其他方面将是不可预测的。

于 2013-02-12T16:10:17.347 回答
2

C 可能不会为循环内声明的变量提供新的内存块。可以使用相同的存储器。您看到重叠输出的原因是因为相同的内存区域应该主要传递给所有线程,并且它也同时在主线程中得到更新。尽管您声明了一个新变量,但新变量没有必要获得一个新的内存块。内存块可以重复使用。这是无法预测的。

此外,这种编码是危险的,因为一旦循环结束,变量将超出范围,当其中一个线程尝试访问它时,这也可能导致未定义的行为。

要确定是否分配了相同的内存,您为什么不尝试打印您传递给每个线程中的线程的 a1 的地址。这将提供最好的清晰度。

于 2013-02-12T16:14:22.203 回答