简体   繁体   English

Linux版C语言中的线程问题

[英]Issue with Threading in C for Linux

Here is what I need to do: 这是我需要做的:

Write a pthread program that takes an integer command line argument n, spawns n threads that will each generate a random numbers between -100 and 100, and then computes and prints out the sum of these random numbers. 编写一个带有整数命令行参数n的pthread程序,生成n个线程,每个线程将生成-100到100之间的随机数,然后计算并打印出这些随机数的总和。 Each thread needs to print out the random number it generates. 每个线程都需要打印出它生成的随机数。

Here is what I have: 这是我所拥有的:

#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;
}

For some reason randomSum is not getting printed. 由于某些原因,randomSum无法打印。

randomNum is a variable that is shared among all threads, so you need a mutex when you access the variable, because randomSum+=randomNum; randomNum是在所有线程之间共享的变量,因此在访问该变量时需要使用互斥锁,因为randomSum+=randomNum; is not an atomic operation. 不是原子操作。 The current process might get interrupted and another process is scheduled which changes both variables. 当前进程可能会中断,并计划另一个进程来更改两个变量。 When the interrupted process resumes, it will overwrite randomNum and you end up with garbage. 当被中断的进程恢复时,它将覆盖randomNum ,最终导致出现垃圾。

Also you have to wait for all threads to finish until you print the sum. 另外,您必须等待所有线程完成,直到您打印总和。 For that you have to execute pthread_wait . 为此,您必须执行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;
}

You really need to learn how to use the libraries you are using. 您确实需要学习如何使用正在使用的库。 pthread_exit must be used by the threads to tell the system "I'm finished", calling it in the main thread makes no sense. 线程必须使用pthread_exit告诉系统“我完成了”,在主线程中调用它是没有意义的。

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

I consider this an uggly hack, what you should do is create an array with the ids of the threads and pass every thread a pointer to its id, like this: 我认为这是一个很棘手的技巧,您应该做的是使用线程的ID创建一个数组,然后将每个指向其ID的指针传递给每个线程,如下所示:

int ids[numThreads];

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

and in the thread you can do 在线程中你可以做

void *randomNumberGenerator(void *idp) {

    int *id = idp;

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

    ...

    pthread_exit(NULL);
}

And if your worker threads are just calculating a value, you can use pthread_exit to return that value back to the main thread. 而且,如果您的工作线程只是在计算一个值,则可以使用pthread_exit将该值返回给主线程。 For example: 例如:

#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;
}

Which gives me the output (for 5 threads): 这给了我输出(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

You currently have a data race in place, because you have multiple threads accessing randomSum concurrently. 您当前有一个数据竞赛,因为您有多个线程同时访问randomSum。 Here's a solution, with comments, using Mutexes to solve the problem. 这是一个使用互斥体解决问题的解决方案,并带有注释。

Note how using a struct to hold the sum and it's mutex allows us to get rid of all globals. 请注意,如何使用结构来保存总和及其互斥量使我们摆脱所有全局变量。

As a plus, I replaced your random generator with a proper one on POSIX systems. 另外,我在POSIX系统上用适当的发生器替换了随机发生器。 Note that your multiple calls to srand() are wrong, and cause less randomicity. 请注意,您对srand()多次调用是错误的,并且会减少随机性。 You should only ever call srand() once, to generate the seed. 您应该只调用一次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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM