多线程程序 - 核心转储

multi threaded program - core dumped

我正在尝试制作一个多线程素数计数器程序。我已经添加了关键代码以供理解(parseargs 函数在另一个程序中工作得很好,感觉没有必要,我不想让你用代码超载)。

长话短说,该程序编译并适用于小数字(最多 1000 个最大值和 8 个线程),但是当我尝试使用更大的数字时,我不断收到很多错误,例如:

分段错误:核心已转储,浮点异常,realloc():下一个大小无效。

代码有什么问题?我知道我应该使用更少的全局变量,但我正在努力让它发挥作用,然后我会变得更干净、更好。 我唯一使用 realloc 的地方是在 isprime 内部;在我的其他程序中效果很好。那里有什么问题?

        #include <ctype.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <math.h>
        #include <unistd.h>
        #include <pthread.h>
        #include <sys/mman.h>
        
        void parseargs(char *argv[], int argc, int *lval, int *uval, int *nval, int *tval);
        int isprime(int n);
        void setFlags();
        
        pthread_mutex_t num_lock;
        pthread_mutex_t count_lock;
        
        char *flagarr = NULL;
        int num;
        int lval = 1;//min value
        int uval = 100;//max value
        int count = 0;//primes counter
        
        int main (int argc, char **argv)
        {
          int nval = 10;// how much numbers to print
          int tval = 4;// threads number
          num = lval;
          
            if (pthread_mutex_init(&num_lock, NULL) != 0) {
                printf("\n mutex init has failed\n");
                return 1;
            } 
               if (pthread_mutex_init(&count_lock, NULL) != 0) {
                printf("\n mutex init has failed\n");
                return 1;
            }
        
          // Parse arguments
          parseargs(argv, argc, &lval, &uval, &nval, &tval);// works 100% (change the defult starting vals)
          if (uval < lval)
          {
            fprintf(stderr, "Upper bound should not be smaller then lower bound\n");
            exit(1);
          }    
          if (lval < 2)
          {
            lval = 2;
            uval = (uval > 1) ? uval : 1;
          }
          if (tval<1)
            tval=4;
        
          // Allocate flags
          flagarr= (char *)malloc(sizeof(char) * (uval-lval+1));
          if (flagarr == NULL)
            exit(1);
            
             //Allocate threads
          pthread_t* t = (pthread_t *)malloc(sizeof(pthread_t)*tval);
             if (t == NULL)
              exit(1);
              
         //start threads
          for(int i =0; i<tval ; i++){
             printf("%d\n",i);
             pthread_create(&t[i],NULL,(void*)setFlags,NULL);
             }
             
         for(int i =0; i<tval ; i++)
             pthread_join(t[i],NULL);
        
        
        
          // Print results
          nval=count;
          printf("Found %d primes from %d to %d%c", count, lval,uval , count ? ' ' : '.');
          if(count && nval>0)
            printf("and printing the first %d of them:\n",nval);
          else
            printf("\n");
          for (num = lval; (num <= uval) && (nval) ; num++)
            if (flagarr[num - lval])
            {
              nval--;
              count--;
              printf("%d%c", num, (count && nval) ? ',' : '\n');  
            }
            free(flagarr);
            free(t);
              return 0;
        }
        
        void setFlags()
        {
         int myNum;
          for (; num <= uval;)
          { 
                pthread_mutex_lock(&num_lock);
                myNum=num;
                num++;
                pthread_mutex_unlock(&num_lock);
                
            if (isprime(myNum))
            {
              flagarr[myNum - lval] = 1; 
              pthread_mutex_lock(&count_lock);
              count ++;
                pthread_mutex_unlock(&count_lock);
            } else {
              flagarr[myNum - lval] = 0; 
            }
          }
        }
int isprime(int n)
{
  static int *primes = NULL;    // NOTE: static !
  static int size = 0;      // NOTE: static !
  static int maxprime;      // NOTE: static !
  int root;
  int i;

  // Init primes array (executed on first call)
   pthread_mutex_lock(&first_lock);
  if (primes == NULL)
  {
    primes = (int *)malloc(2*sizeof(int));
    if (primes == NULL)
      exit(1);
    size = 2;
    primes[0] = 2;
    primes[1] = 3;
    maxprime = 3;
  }
  pthread_mutex_unlock(&first_lock);

  root = (int)(sqrt(n));

  // Update primes array, if needed
  while (root > maxprime)
    for (i = maxprime + 2 ;  ; i+=2)
      if (isprime(i))
      {
         pthread_mutex_lock(&primeFunc_lock);
        size++;
        primes = (int *)realloc(primes, size * sizeof(int));
        if (primes == NULL)
          exit(1);
        primes[size-1] = i;
        maxprime = i;
        pthread_mutex_unlock(&primeFunc_lock);
        break;
      }

  // Check 'special' cases
  if (n <= 0)
    return -1;
  if (n == 1)
    return 0;

  // Check prime
  for (i = 0 ; ((i < size) && (root >= primes[i])) ; i++)
    if ((n % primes[i]) == 0)
      return 0;
  return 1;
}

I know that I should use fewer global variables but I'm trying to make it work and then I will make cleaner and better.

对于多线程程序,这种方法行不通 -- 你不能先写一个有问题的程序,然后再把它变得漂亮,然后 bug-free -- 你 必须 先拿到bug-free

此外,全局和静态变量以及线程通常不会混用。

现在,正如其他人所指出的,您可以在 isprime() 中访问静态变量而无需任何锁定。想想线程T1执行这段代码会发生什么:

  for (i = 0 ; ((i < size) && (root >= primes[i])) ; i++)
    if ((n % primes[i]) == 0)

另一个线程T2同时执行这条语句:

        primes = (int *)realloc(primes, size * sizeof(int));

由于 primes 不是原子变量,编译器(可能)会将 primes 的值加载到某个寄存器中,并在 T1 的整个循环中继续使用该值。循环将完全忘记 primes 可能已经在 T2 中被 free()d 和 realloc()ed,并且现在指向完全不同的内存块这一事实。 T1 将继续读取旧的(现在悬空的)内存。

您的 isprimes() 函数中存在类似的问题,很容易解释您观察到的崩溃。

如果您在 Linux 系统上,Thread sanitizer (and Address sanitizer) 是您的朋友。