C/多线程/分段错误/(可能是)线程队列问题

Posted

技术标签:

【中文标题】C/多线程/分段错误/(可能是)线程队列问题【英文标题】:C/Multithreading /Segmentation fault / (May be) Issue with queue for the threads 【发布时间】:2014-09-02 02:20:52 【问题描述】:

我正在尝试创建线程库。为此,我正在尝试实现队列来存储待执行的待执行线程。

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

   typedef struct 
       ucontext_t context;
   MyThread;

   #define MAX 20
   MyThread queue[MAX];
   int rear=0,front=0;

   void addToQueue(MyThread t)
   
       if(rear==MAX)
       
           printf("Queue is full!");
           return;
               
       queue[front]=t;
       front+=1;
   

   MyThread* removeFromQueue()
          
       if(front==rear)
       return NULL;        
       rear=rear+1;
       return &(queue[rear-1]);       
   

   MyThread umain;

   void MyThreadInit (void(*start_funct)(void *), void *args)
   
    getcontext(&(umain.context));
    char p[64000];
       umain.context.uc_stack.ss_sp =(char *)p;
       umain.context.uc_stack.ss_size = sizeof(p);
       umain.context.uc_link =NULL;
       makecontext(&umain.context,(void(*)(void))start_funct,1,args);
       setcontext(&(umain.context));

   

    MyThread MyThreadCreate (void(*start_funct)(void *), void *arg)
   
         MyThread newthread;
       char args[10000];
        getcontext(&(newthread.context));
        newthread.context.uc_stack.ss_sp =(char *)args;
        newthread.context.uc_stack.ss_size = sizeof(args);
        newthread.context.uc_link =NULL;
        makecontext(&newthread.context,(void(*)(void))start_funct,1,arg);
        addToQueue(newthread);

        return newthread;
             
    void MyThreadYield(void)
    
        MyThread* a=removeFromQueue();
        MyThread save;
        if(a != NULL)
        
         printf("Before yielding the context \n");

         getcontext(&(save.context));
         addToQueue(save);
         //swapcontext(&umain.context,&(a->context));
         setcontext(a);    
         printf("After the swapping the context \n");
        
        else
         printf("NULL!!! \n");
        
    

    void func1(void *arg)
    
     printf("func1started \n");        
     MyThreadYield();
    

    void func2(void *arg)
    
     printf("func2started \n");
     MyThreadYield();         
    
    void func12(void *arg)
    
     printf("func12started \n");
     MyThreadCreate(func1,arg);
     MyThreadCreate(func2,arg);
     MyThreadYield();

    

    int main(void)
    
        int i=0;
        printf("inside the main function \n");
        MyThreadInit(func12,&i);

        return 0;
    

     Output :
     inside the main function
     func12started
     Before yielding the context
     func1started
     Before yielding the context
     func2started
     Before yielding the context
     func1started
     Before yielding the context
     Segmentation fault

之所以提到队列,是因为我尝试通过从“MyThreadYield”函数中删除以下代码进行试验,它工作正常,但没有达到预期的功能。 getcontext(&(save.context)); addToQueue(保存);

【问题讨论】:

你有什么问题?你得到什么错误?你期待什么? 没有达到预期的功能是什么意思? 您的队列不可能工作。您需要阅读如何编写好的并行对象。即使你修复了你的记忆错误,它也会给你错误的东西。 我得到的错误:(在代码末尾的输出中已经提到)分段错误。 我的问题是为什么会出现分段错误?在询问之前,我尝试删除一些与队列相关的代码。它工作得很好。因此,我认为它在那个区域附近。上面提到了预期的功能。能够在屈服功能的情况下存储线程。 预期流程是:Main-> umain 线程-> umain 屈服于线程 1 并存储在队列中-> 线程 1 屈服于 thread2 并存储在队列中 -> 线程 2 屈服于 umain -> umain从上次离开开始执行->打印(交换线程后)->成功退出。 【参考方案1】:

首先,此时您的队列实现不是线程安全的。您的问题强烈表明此代码将在多线程环境中使用。拥有一个非线程安全的队列会给你错误的结果,并且可能会发生奇怪的事情(比如removeFromQueue() 将相同的东西返回给两个不同的线程,或者addToQueue() 在相同的位置插入两个项目)。

除此之外,您的队列实现将永远无法工作。您没有正确使用 frontrear。仔细看插入函数:

void addToQueue(MyThread t)

    if (rear==MAX)
    
        printf("Queue is full!");
        return;
            
    queue[front]=t;
    front+=1;

您检查rear 是否为MAX,但是,您写入queue[front] 并增加front。如果我只是继续向队列中添加项目,最终达到缓冲区的限制怎么办? rear 将始终为 0,front 将无限增长,并且您的函数将超出 queue 的限制。这可能是您的分段错误错误的原因。

我认为您想改为检查 front

void addToQueue(MyThread t)

    if (front == MAX)
    
        printf("Queue is full!");
        return;
            
    queue[front]=t;
    front+=1;

removeFromQueue() 的代码表面上看起来没问题,只要queue 是一个全局数组(因为您要返回一个指针,并且不能返回指向局部变量的指针)。但是,您必须从这个答案中得出一个最重要的事实,即您的队列实现不会长期扩展。基于数组的队列是一个糟糕的选择。当阵列中的空间不足时,您会怎么做?如果我插入 MAX 元素,然后删除 2 或 3,并尝试插入更多怎么办?您的代码会说队列已满,因为它只允许您总共插入 MAX 元素。当一个元素被移除时,你可以将队列中的每个元素都向左移动,但这很疯狂,而且效率极低。或者您可以增加frontMAX,允许rear 领先于front,只要您知道最多可以插入MAX 元素。这会更好,但它会破坏removeFromQueue() 中的逻辑,因为在您操作队列时,较早返回的指针可能会指向不同的线程结构 - 完全是灾难。绝对不是你想要的。

更好的方法是使用链表来实现这一点,在链表中保留指向头部的指针和指向尾部的指针。看看http://en.wikipedia.org/wiki/Queue_(abstract_data_type)#Queue_implementation

【讨论】:

以上是关于C/多线程/分段错误/(可能是)线程队列问题的主要内容,如果未能解决你的问题,请参考以下文章

警报处理程序中交换上下文后的分段错误

C++ std 线程和列表分段错误(核心转储)

多线程程序中的分段错误和 gdb 回溯信息不完整

Boost 线程中的分段错误 tls_destructor

libcurl 中的分段错误,多线程

C中多线程python扩展中的段错误