C for Linux中的线程问题

问题描述 投票:0回答:2

这是我需要做的:

编写一个带有整数命令行参数n的pthread程序,生成n个线程,每个线程生成一个介于-100和100之间的随机数,然后计算并打印出这些随机数的总和。每个线程都需要打印出它生成的随机数。

这是我有的:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
int randomNum=0;
int randomSum=0;
void *randomNumberGenerator(void *id){
    int *myid = (int *)id;
    randomNum = rand()% 201 + (-100);
    printf("%d\n", randomNum);
    randomSum+=randomNum;

}

int main (int argc , char *argv[]){
    int command;
    char *strNumThreads = NULL;
    int i;
    while((command = getopt(argc, argv, "n:"))!=-1){
        if(command == 'n'){
            strNumThreads = optarg;
            break;
        }
    }

    int numThreads = atoi(strNumThreads);
    pthread_t thread;
    int newThread;

    for(i = 0; i<numThreads; i++){
        srand(time(NULL));
        pthread_create(&thread, NULL, randomNumberGenerator, (void*)i); 

    }
    pthread_exit(NULL);
    printf("%d\n" , randomSum);

    return 0;
}

由于某种原因,randomSum没有打印出来。

c linux
2个回答
1
投票

randomNum是一个在所有线程之间共享的变量,因此在访问变量时需要一个互斥锁,因为randomSum+=randomNum;不是原子操作。当前进程可能会中断,并且会安排另一个更改这两个变量的进程。当中断的进程恢复时,它将覆盖randomNum并最终导致垃圾。

此外,您必须等待所有线程完成,直到您打印总和。为此你必须执行pthread_wait

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>

// can be a global variable
int randomSum=0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *randomNumberGenerator(void *id){
    int randomNum=0; // does not need to be a global variable
    randomNum = rand()% 201 + (-100);
    printf("%d\n", randomNum);
    pthread_mutex_lock(&mutex);
    randomSum+=randomNum;
    pthread_mutex_unlock(&mutex);

    pthread_exit(0);
}

int main (int argc , char *argv[]){
    int command;
    char *strNumThreads = NULL;
    int i;
    while((command = getopt(argc, argv, "n:"))!=-1){
        if(command == 'n'){
            strNumThreads = optarg;
            break;
        }
    }

    // initializing the randomizer
    srand(time(NULL));

    int numThreads = atoi(strNumThreads);
    if(numThreads == 0)
    {
        fprintf(stderr, "Invalid number of threads\n");
        return 1;
    }

    pthread_t threads[numThreads];

    for(i = 0; i<numThreads; i++){
        pthread_create(threads + i, NULL, randomNumberGenerator, NULL); 
    }

    for(i = 0; i < numThreads; ++i)
        pthread_join(threads[i], NULL);


    printf("%d\n" , randomSum);

    return 0;
}

您真的需要学习如何使用您正在使用的库。线程必须使用pthread_exit来告诉系统“我已经完成”,在主线程中调用它是没有意义的。

pthread_create(&thread, NULL, randomNumberGenerator, (void*)i); 

我认为这是一个uggly hack,你应该做的是创建一个带有线程ID的数组,并向每个线程传递一个指向其id的指针,如下所示:

int ids[numThreads];

for(i = 0; i<numThreads; i++){
    ids[i] = i;
    pthread_create(&thread, NULL, randomNumberGenerator, ids+i);
}

在线程中你可以做到

void *randomNumberGenerator(void *idp) {

    int *id = idp;

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

    ...

    pthread_exit(NULL);
}

如果你的工作线程只是计算一个值,你可以使用pthread_exit将该值返回给主线程。例如:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>

struct thdata {
    int id;
    int random;
};


void *randomNumberGenerator(void *data) {
    struct thdata *ret = data;

    ret->random = rand()% 201 + (-100);
    printf("thread with id %d: random %d\n", ret->id, ret->random);

    pthread_exit(data);
}

int main (int argc , char *argv[]){
    int i;

    // initializing the randomizer
    srand(time(NULL));

    int numThreads = 5;
    if(numThreads == 0)
    {
        fprintf(stderr, "Invalid number of threads\n");
        return 1;
    }

    pthread_t threads[numThreads];
    struct thdata data[numThreads];

    for(i = 0; i<numThreads; i++){
        data[i].id = i;
        pthread_create(threads + i, NULL, randomNumberGenerator, data+i); 
    }

    int randomSum = 0;
    for(i = 0; i < numThreads; ++i)
    {
        struct thdata *data;
        pthread_join(threads[i], (void**) &data);
        randomSum += data->random;
    }


    printf("The sum of the random values is: %d\n" , randomSum);

    return 0;
}

这给了我输出(5个线程):

thread with id 0: random 72
thread with id 4: random -94
thread with id 1: random 1
thread with id 2: random -74
thread with id 3: random 42
The sum of the random values is: -53

1
投票

您目前正在进行数据竞争,因为您有多个线程同时访问randomSum。这是一个解决方案,带有注释,使用互斥锁来解决问题。

注意如何使用结构来保存总和,它的互斥量允许我们摆脱所有全局变量。

作为一个加号,我在POSIX系统上用适当的随机生成器替换了你的随机生成器。请注意,您对srand()的多次调用是错误的,并且导致较少的随机性。你应该只调用一次srand()来生成种子。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <limits.h>
#include <time.h>
#include <pthread.h>

static bool HAS_URANDOM = true; // Global

unsigned int random_uint() {
    unsigned int r_uint;
    // Try to open the random generator device
    FILE *f = fopen("/dev/urandom", "r");
    if (f == NULL) {
        if (HAS_URANDOM) {
            // Warn that urandom isn't working, but fallthrough to rand()
            printf("---- Failed loading random generator device /dev/urandom. Defaulting to rand().\n");
            srand((unsigned int) time(NULL));
            HAS_URANDOM = false;
        }
        r_uint = (unsigned int) rand();
    } else {
        // If we have urandom, just read from it and cast to uint
        fread(&r_uint, sizeof(r_uint), 1, f);
        fclose(f);
    }
    return r_uint;
}

// Inclusive range
// https://stackoverflow.com/a/17554531/2080712
unsigned int generate_uint(unsigned int lower, unsigned int upper) {
    if (upper - lower == UINT_MAX) {
        fprintf(stderr, "Invalid bounds on generate_int().\n");
        exit(EXIT_FAILURE);
    }
    unsigned int r_uint;
    const unsigned int range = 1 + (upper - lower);
    if (range == 0) {
        fprintf(stderr, "Invalid range!\n---- upper=%d\n---- lower=%d\n---- range=%d\n", upper, lower, range);
        exit(EXIT_FAILURE);
    }
    const unsigned int buckets = UINT_MAX / range;
    const unsigned int limit = buckets * range;
    /* Create equal size buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    do {
        r_uint = random_uint();
    } while (r_uint >= limit);
    unsigned int res = lower + (r_uint / buckets);
    return res;
}

typedef struct {
    pthread_mutex_t lock; // Our lock to avoid data races
    long sum; // The sum value
} sum_t;

// Thread function
void *do_sum(void *arg) {
    sum_t *sum = (sum_t*)(arg); // Reinterpret the argument as sum_t
    int val = generate_uint(0, 100) - 100; // Generate an integer in the range we want
    pthread_mutex_lock(&sum->lock); // Lock the value
    sum->sum += val; // Sum
    pthread_mutex_unlock(&sum->lock); // Unlock the value
    return NULL;
}

int main(int argc, char *argv[]) {
    // Guarantee argument
    if(argc != 2) {
        printf("Please provide a number of threads.\n");
        exit(EXIT_FAILURE);
    }
    // Get our thread count
    long count = strtol(argv[1], NULL, 10);

    // Allocate threads
    pthread_t threads[count];

    // Create & initialize sum structure
    sum_t sum;
    pthread_mutex_init(&(sum.lock), NULL);
    sum.sum = 0;

    // Run sum threads
    for (long i = 0; i < count; ++i) {
        pthread_create(&(threads[i]), NULL, do_sum, &sum);
    }

    // Wait until they have finished
    for (long i = 0; i < count; ++i) {
        pthread_join(threads[i], NULL);
    }

    // Destroy the mutex lock
    pthread_mutex_destroy(&(sum.lock));

    // Print result
    printf("%ld\n", sum.sum);
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.