使用 pthreads 来加速从 0 到 N 计算质数的处理。我使用它们是否正确?

Using pthreads to speed up the processing of counting prime numbers from 0 to N. Am I using them correctly?

我正在编写代码来计算从 0 到 N 的质数,利用 8 个 pthreads 来加速这个过程。我已经在线研究了 C 中的多线程,但我仍然不确定在这种情况下我是否正确使用了它们。他们真的在加快我程序的执行时间吗?如果我没记错的话,pthread是同时执行他们的函数的吧?

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

#define NUM_COUNT 800
#define NUM_THREADS 8

int counter = 0; //counter to count primes
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//function to find primes
int is_prime(int n) {
    if (n < 2)return 0;
    if (n == 2)return 1;
    if (n % 2 == 0)return 0;
    for (int i=3; i < n; i += 2) {
        if (n % i == 0) return 0;
    }
    return 1;
}

void *PrintPrimes(void *threadid) {
    int thread_start, thread_end;
    int thread_id = (int)threadid; //store thread id in thread_id
    thread_start = thread_id*(NUM_COUNT/NUM_THREADS); //determine where individual thread begins searching for primes
    thread_end = thread_start+(NUM_COUNT/NUM_THREADS); //determine where thread ends searching for primes
    for(int n = thread_start; n < thread_end; n++) {
        if (is_prime(n)) {
            pthread_mutex_lock(&mutex);
            counter++;
            printf("the number of primes is currently %d\n", counter);
            pthread_mutex_unlock(&mutex);
        }
    }
    pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
    pthread_t threads[NUM_THREADS];
    for(int i = 0; i < NUM_THREADS; i++){
        pthread_create(&threads[i], NULL, PrintPrimes, (void *)i);
    }
    pthread_exit(NULL);
}

我发现代码有一些问题:

  1. 您永远不会在线程上调用 pthread_join(),这意味着您的程序将在生成线程后立即退出,而不是等待它们完成——这可能不是您想要的。您应该在 main() 函数的底部添加第二个像这样的 for 循环:

     for(int i = 0; i < NUM_THREADS; i++) {
         pthread_join(&threads[i], NULL);
     }
    
  2. main()中对pthread_exit()的调用是不必要的,可以去掉。 (它意味着从生成的 pthread 中调用以导致线程退出,没有必要从主线程调用它)

  3. 从线程的计算循环中调用 printf() 会大大减慢计算速度(以至于您根本不再测量实际计算的性能) ,而您实际上只是在测量 printf() 和 stdout 子系统执行的速度)

  4. 每次找到一个新的质数时都必须用互斥锁来保护 shared/global counter 效率不是很高;最好为每个线程声明一个 local/non-shared 计数器变量,并递增它。然后在线程执行结束时,您可以将线程的本地计数器添加到 shared/global 计数器一次,从而避免更多地支付 lock()/unlock() 序列带来的同步惩罚每个线程不止一次。