pthreads 没有被调用或被调用多次没有明显的原因

pthreads not getting called or getting called multiple times with no apparent reason

我正在尝试编写一个多线程 C 程序来查找区间内的所有质数。

单线程版本运行完美,但多线程版本存在重要问题,尤其是线程生命周期引起的问题。

这是代码:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>

#define THREADS_NUM 4

struct ipp_args{
    int id;     // ID of the thread
    int stop;   // Max number to test
};

void *prime_checker(void*);

pthread_t threads[THREADS_NUM];
int count = 0;

int main(int argc, char const *argv[]) {
    int start, end;

    if (argc == 1) {
        start = 2;
        end = 100;
    } else if (argc == 3) {
        start = atoi(argv[1]);
        end = atoi(argv[2]);
    } else {
        printf("Usage:\n\tprimes <from> <to>\n");
        exit(1);
    }
    if (start < 2) start = 2;
    
    // Launch threads
    for (int thread_id = 0; thread_id < THREADS_NUM; ++thread_id) {
        struct ipp_args args;
        args.id = thread_id;
        args.stop = end;
        
        printf("[MAIN] Starting T_%d\n", thread_id);
        int error = pthread_create(&threads[thread_id], NULL, prime_checker, (void*)&args);
        if (error) {
            printf("[MAIN] Cannot create thread %d\n", thread_id);
            exit(3);
        }
    }

    // Join threads
    for (int thread_id = 0; thread_id < THREADS_NUM; thread_id++) {
        printf("[MAIN] Joining T_%d\n", thread_id);
        int error = pthread_join(threads[thread_id], NULL);
        if (error) {
            printf("[MAIN] Cannot join thread %d\n", thread_id);
            exit(4);
        }
    }

    printf("%d primes found\n", count);

    pthread_exit(NULL);
    return 0;
}

void *prime_checker(void *_args) {
    struct ipp_args *args = _args;
    int id = args->id;
    int stop = args->stop;
    printf("[T_%d] Started!\n", id);

    for (int n = 2 + id; n < stop; n += THREADS_NUM) {
        //printf("[T_%d] Checking %d\n", id, n);
        int limit = sqrt(n);
        bool prime = true;
        for (int i = 2; i <= limit; i++) {
            if (n % i == 0) {
                prime = false;
                break;
            }
        }
        if (prime) count++;
    }

    pthread_exit(NULL);
    return NULL;
}

运行 代码 returns 每个 运行 的不同输出,这里有一些例子:

[MAIN] Starting T_0
[MAIN] Starting T_1
[MAIN] Starting T_2
[MAIN] Starting T_3
[MAIN] Joining T_0
[T_3] Started!
[T_3] Started!
[T_3] Started!
[T_3] Started!
[MAIN] Joining T_1
[MAIN] Joining T_2
[MAIN] Joining T_3
44 primes found
[MAIN] Starting T_0
[MAIN] Starting T_1
[T_1] Started!
[T_2] Started!
[MAIN] Starting T_2
[MAIN] Starting T_3
[MAIN] Joining T_0
[T_3] Started!
[MAIN] Joining T_1
[MAIN] Joining T_2
[T_3] Started!
[MAIN] Joining T_3
35 primes found
[MAIN] Starting T_0
[MAIN] Starting T_1
[MAIN] Starting T_2
[T_2] Started!
[T_2] Started!
[MAIN] Starting T_3
[T_3] Started!
[MAIN] Joining T_0
[T_3] Started!
[MAIN] Joining T_1
[MAIN] Joining T_2
[MAIN] Joining T_3
22 primes found

有些线程有时会被调用多次,而另一些则根本不会被调用。 如果有人知道如何解决这个问题或可能是什么原因,请告诉我。

两个问题:

    main() 中的
  • struct ipp_args args; 是 main() 中循环中的局部变量,一旦创建所有线程,它就会超出范围。您需要创建它 static 或动态分配它。

  • count 不受竞争条件的保护,并且不能假定对它的访问是原子的,因此可能会发生各种奇怪的事情,特别是当两个线程试图更新它时同时

至于素数算法,让它迭代偶数然后包括一个昂贵的检查是否一个数字在循环内是非常慢的。 for循环的一个不那么天真的版本是for (int i = 1; i <= limit; i+=2),那么你不需要担心偶数。