【问题标题】:Segmentation fault while creating multiple threads in C在 C 中创建多个线程时出现分段错误
【发布时间】:2021-08-08 12:50:29
【问题描述】:

我正在尝试创建 1000000 多个线程并让它们从队列中添加和删除元素。 队列的大小为 10 个元素,当它已满并想要添加一个元素时,它会等待来自 Condition 的信号,当它为空并尝试删除元素时也是如此。

当我创建 100000 个线程时,代码运行良好,但是当我尝试添加一个或多个零时,它给了我一个分段错误。

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

#define SIZE 10

long NBTHREADS = 1000000;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t notFull = PTHREAD_COND_INITIALIZER;
pthread_cond_t notEmpty = PTHREAD_COND_INITIALIZER;

long sum = 0;

typedef long element;
typedef struct queue{
    element data[SIZE];
    int front, rear;
}queue;

queue CreateQueue(){
    queue q;
    q.front = 1;
    q.rear = 0;
    return q;
}
queue list;

int isEmptyQueue(queue q) {
    return (q.front == (q.rear + 1) % SIZE);
}

int isFullQueue(queue q) {
    return (q.front == (q.rear + 2) % SIZE);
}

int EnQueue(queue *q,element e) {
    q->rear = (q->rear + 1) % SIZE;
    q->data[q->rear] = e;
    return 1;
}

int DeQueue(queue *q) {
    q->front = (q->front + 1) % SIZE;
    return 1;
}

int Front(queue q,element *e) {
    if (isEmptyQueue(q))return 0;
    *e = q.data[q.front];
    return 1;
}

void PrintQueue(queue q){
    element e;
    while(!isEmptyQueue(q) && Front(q, &e)){
        printf("%ld ", e);
        DeQueue(&q);
    }
    printf("\n");
}

void *addToList(void *num)
{
    long number = (long)num;
    pthread_mutex_lock(&lock);
    while(isFullQueue(list))
        pthread_cond_wait(&notFull, &lock);
    EnQueue(&list, number);
    pthread_cond_signal(&notEmpty);
    sum++;
    pthread_mutex_unlock(&lock);
    pthread_exit(0);
}

void *deleteFromList()
{
    pthread_mutex_lock(&lock);
    while(isEmptyQueue(list))
        pthread_cond_wait(&notEmpty, &lock);
    DeQueue(&list);
    pthread_cond_signal(&notFull);
    sum++;
    pthread_mutex_unlock(&lock);
    pthread_exit(0);
}

int main()
{
    list = CreateQueue();
    void *status;
    pthread_attr_t attr;
    pthread_t threads[NBTHREADS];
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for(long i=0; i<NBTHREADS; i++)
    {
        long err;
        if(i<(NBTHREADS/2))
        {
            err = pthread_create(&threads[i], &attr, addToList, (void*)i);
        }else{
            err = pthread_create(&threads[i], &attr, deleteFromList, NULL);
        }
        if(err){
            printf("\nError Creating Thread...");
            exit(-1);
        }
    }
    pthread_attr_destroy(&attr);
    for(long i=0; i<NBTHREADS; i++)
    {
        long err = pthread_join(threads[i], &status);
        if(err)
            printf("\nFAILED TO JOIN MAIN WITH THREAD %ld", (long)status);
        else
            printf("\nsuccessfully joined");
    }
    printf("\nSum is: %ld", sum);

    PrintQueue(list);
    pthread_exit(NULL);

}

它给了我这个错误: Process returned -1073741571 (0xC00000FD) execution time : 9.364 s

我还是多线程和并行编程的新手。
也许我在错误的地方使用了锁?
但问题是,当我将 NBTHREADS 变量设置为 100000 或更少时,程序执行良好并返回 0,并且队列没有打印任何内容(它是空的)。
我不知道我的问题出在哪里,感谢您的帮助。

【问题讨论】:

  • 那是……很多线程。可能超出您的系统支持的范围。
  • 或者你的 threads 数组变得太大导致堆栈溢出。
  • 在 C 中,您不应该将大数组作为“局部变量”。所以不要这样做:pthread_t threads[NBTHREADS]; 要么将其作为(丑陋的)全局变量,要么使用 malloc 分配它
  • 我的两分钱,您的queue CreateQueue() 方法在其堆栈上创建一个queue 结构并返回它以初始化您的list 静态queue 结构。一种更有效的方法是将list 上的指针传递给CreateQueue 并让它直接初始化您的静态list(或直接使用静态结构方法对其进行初始化),此处堆栈上的数据交换似乎没有用,因为你list 对象已经有它的保留空间。作为参考,在 x86 上,gcc 的常见堆栈限制接近 100 万个指针,这应该可以解释您的实际问题。
  • @KamelYehya 答案已更新为代码示例。试试看,如果有帮助,请告诉我们

标签: c multithreading parallel-processing segmentation-fault pthreads


【解决方案1】:

可能有其他系统限制阻止 1.000.000 个线程,但(很可能)涉及堆栈溢出问题。

main 你有:

pthread_t threads[NBTHREADS];

具有自动存储持续时间的数组(通常称为“局部变量”)通常存储在称为堆栈的固定大小内存块中的系统上。如果数组的大小超过了堆栈的大小,就会发生堆栈溢出,程序将无法运行。

在我的系统上 1.000.000 pthread_t 是 8.000.000 字节,高于我系统的(默认)堆栈大小。所以这样的程序在我的系统上会失败。

你可以做(​​至少)三件事:

  1. 将数组变量放在全局空间中(一种丑陋的解决方案)

  2. 使用malloc分配数组

  3. 如果您的系统允许,则增加堆栈大小

对于解决方案 1,请执行以下操作:

//long NBTHREADS = 1000000;
#define NBTHREADS 1000000

pthread_t threads_global[NBTHREADS];

int main()
{
    list = CreateQueue();
    void *status;
    pthread_attr_t attr;
    ////pthread_t threads[NBTHREADS];
    pthread_attr_init(&attr);

并更新代码以使用这个新的“丑陋”变量名

对于解决方案 2,请执行以下操作:

int main()
{
    list = CreateQueue();
    void *status;
    pthread_attr_t attr;
    pthread_t *threads = malloc(NBTHREADS * sizeof *threads);
    assert(threads != NULL);  // Or: if (threads == NULL) exit(1);

对于解决方案 3:

取决于你的系统

我会选择解决方案 2。

【讨论】:

  • 当我尝试将其用作全局变量时,出现以下编译错误:variably modified 'threads_global' at file scope.
  • @KamelYehya 嗯...请尝试解决方案 2
  • 使用全局变量时,您可能必须在编译时将数组大小NBTHREADS 设为常量而不是变量。 #define NBTHREADS 1000000
  • 我认为我的系统无法处理那么多线程......当我尝试使用 malloc 时,系统完全停止响应。感谢您的帮助。
  • @Bodo Nice catch
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-19
  • 1970-01-01
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 2019-08-27
相关资源
最近更新 更多