-2

スレッド ライブラリを作成しようとしています。このために、実行する保留中のスレッドを格納するキューを実装しようとしています。

   #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(保存);

4

1 に答える 1

0

1 つには、キューの実装は現時点ではスレッドセーフではありません。あなたの質問は、このコードがマルチスレッド環境で使用されることを強く示唆しています。スレッドセーフでないキューを使用すると、間違った結果が得られ、おかしなことが発生する可能性があります ( removeFromQueue()2 つの異なるスレッドに同じものを返す、addToQueue()同じ位置に 2 つの項目を挿入するなど)。

それを除けば、キューの実装は決して機能しません。あなたは正しく使用frontしていません。rear挿入関数を注意深く見てください。

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

が であるかどうかを確認しますrearMAX、 に書き込み、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あなたのコードは、要素を合計でしか挿入できないため、キューがいっぱいであると言います。要素が削除されたときにキュー内のすべての要素を左にシフトすることはできますが、それはクレイジーであり、非常に非効率的です。frontまたは、 moduloをインクリメントしてMAX、許可することもできますrear以上の要素を挿入できfrontないことがわかっている限り、よりも先に進みます。MAXそれはより良いことremoveFromQueue()ですが、キューを操作すると、以前に返されたポインターが別のスレッド構造体を指している可能性があるため、 のロジックが壊れてしまいます。間違いなくあなたが望むものではありません。

はるかに優れたアプローチは、リンクされたリストを使用してこれを実装し、先頭へのポインターと末尾へのポインターを保持することです。http://en.wikipedia.org/wiki/Queue_(abstract_data_type)#Queue_implementationをご覧ください。

于 2014-09-02T17:58:26.820 に答える