【问题标题】:pthreads not getting called or getting called multiple times with no apparent reasonpthreads 没有被调用或被多次调用,没有明显的原因
【发布时间】:2021-11-20 22:11:17
【问题描述】:

我正在尝试编写一个多线程 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;
}

运行代码会在每次运行时返回不同的输出,以下是一些示例:

[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

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

【问题讨论】:

  • 常见错误。 struct ipp_args args; 的生命周期为 一次 迭代。变量在每次迭代结束时变为无效。到线程看到变量时,它可以具有任何不确定的值。需要在循环外声明这些结构的数组,并且需要将不同的地址传递给每个线程。
  • 您可能会考虑使用 OpenMP 来并行化您现有的单线程算法。
  • 这不是并行性,而是多个线程执行相同的事情,然后将它们的结果相加。

标签: c multithreading pthreads


【解决方案1】:

两个问题:

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

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

至于素数算法,它迭代偶数非常慢,然后包含一个昂贵的检查一个数字是否在循环内。 for 循环的一个不那么幼稚的版本是for (int i = 1; i &lt;= limit; i+=2),那么您不必担心偶数。

【讨论】:

  • 感谢您的建议,修复了ipp_args后就可以了,比单线程快10倍。我还为线程结果创建了一个数组,以避免同时访问同一地址的内存。也感谢您提出改进循环的建议,我的问题是:如果它从 i=1 开始,它在检查 n%i==0 时总是会中断,或者我错过了什么?
  • @TechnoDeveloper 您不需要使用此版本检查 n%i==0,这就是重点。 n%i==0 表示“是否偶数”。你需要检查一个奇数是不是偶数?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-16
  • 1970-01-01
相关资源
最近更新 更多