为什么此C代码导致竞争状态?

时间:2019-05-22 03:13:03

标签: c multithreading pthreads

我正试图计算多达1000万个素数,而我必须使用Posix线程使用多个线程来计算(因此,每个线程计算出1000万个子集)。但是,我的代码没有检查条件IsPrime。我认为这是由于比赛条件造成的。如果可以解决这个问题,我该怎么办?

我尝试使用带有k个元素的全局整数数组,但是由于未定义k,所以不允许我在文件范围内声明它。

我正在使用gcc -pthread运行我的代码:

/*
Program that spawns off "k" threads
k is read in at command line each thread will compute
a subset of the problem domain(check if the number is prime)

to compile: gcc -pthread lab5_part2.c -o lab5_part2
*/
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <stdlib.h>

typedef int bool;
#define FALSE 0
#define TRUE 1

#define N 10000000  // 10 Million

int k; // global variable k willl hold the number of threads
int primeCount = 0; //it will hold the number of primes.

//returns whether num is prime
bool isPrime(long num) {
    long limit = sqrt(num);
    for(long i=2; i<=limit; i++) {
        if(num % i == 0) {
             return FALSE;
        }
     }
     return TRUE;
}

//function to use with threads
void* getPrime(void* input){
    //get the thread id
    long id = (long) input;
    printf("The thread id is: %ld \n", id);

    //how many iterations each thread will have to do
    int numOfIterations = N/k;

  //check the last thread. to make sure is a whole number.
  if(id == k-1){
    numOfIterations = N - (numOfIterations * id);
  }

  long startingPoint = (id * numOfIterations);
  long endPoint = (id + 1) * numOfIterations;
  for(long i = startingPoint; i < endPoint; i +=2){
    if(isPrime(i)){
      primeCount ++;
    }
  }
  //terminate calling thread.
  pthread_exit(NULL);
}

int main(int argc, char** args) {
    //get the num of threads from command line
    k = atoi(args[1]);
    //make sure is working
   printf("Number of threads is: %d\n",k );

   struct timespec start,end;

    //start clock
    clock_gettime(CLOCK_REALTIME,&start);

    //create an array of threads to run
    pthread_t* threads = malloc(k * sizeof(pthread_t));
    for(int i = 0; i < k; i++){
      pthread_create(&threads[i],NULL,getPrime,(void*)(long)i);
    }

    //wait for each thread to finish
    int retval;
    for(int i=0; i < k; i++){
      int * result = NULL;
      retval = pthread_join(threads[i],(void**)(&result));
    }

    //get the time time_spent
    clock_gettime(CLOCK_REALTIME,&end);
    double time_spent = (end.tv_sec - start.tv_sec) +
                    (end.tv_nsec - start.tv_nsec)/1000000000.0f;

    printf("Time tasken: %f seconds\n", time_spent);

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

}

我得到的当前输出:(使用2个线程)

Number of threads is: 2

Time tasken: 0.038641 seconds

2 primes found.

1 个答案:

答案 0 :(得分:2)

计数器primeCount被多个线程修改,因此必须是原子的。要使用标准库(现在POSIX也支持此库)来解决此问题,您应该#include <stdatomic.h>,将primeCount声明为atomic_int,并用atomic_fetch_add()对其进行递增或atomic_fetch_add_explicit()

更好的是,如果您直到最后都不在乎结果,则每个线程可以将自己的计数存储在单独的变量中,一旦线程结束,主线程可以将所有计数加在一起。您将需要在主线程中为每个线程创建一个原子计数器(以便更新不会破坏同一高速缓存行中的其他数据),将每个线程的指针传递给其输出参数,然后将部分计数返回到通过该指针的主线程。

这看起来像是您要解决的练习,所以我不会为您编写代码,但是使用的方法是声明一个计数器数组,例如线程ID数组,并传递{{ 1}}作为&counters[i]的{​​{1}}参数,类似于您传递arg的方式。每个线程将需要其自己的计数器。在线程过程的最后,您将编写类似pthread_create()的内容。在所有现代体系结构上,这应该完全无需等待。

您可能还认为,避免每个线程进行一次原子更新,将&threads[i]声明为atomic_store_explicit( (atomic_int*)arg, localTally, memory_order_relaxed );,然后在线程过程之前进行一次primeCount并不是一件麻烦的事。终止。