我正在尝试创建线程库。为此,我正在尝试实现队列来存储要执行的挂起线程。
#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(&(保存.context));
添加到队列(保存);
最佳答案
首先,您的队列实现此时不是线程安全的。您的问题强烈表明该代码将在多线程环境中使用。使用非线程安全队列会给您带来错误的结果,并且可能会发生奇怪的事情(例如 removeFromQueue()
将相同的内容返回到两个不同的线程,或者 addToQueue()
在同一位置插入两个项目)。
除此之外,您的队列实现将永远无法工作。您没有正确使用 front
和 rear
。仔细看插入函数:
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
元素。当删除一个元素时,您可以将队列中的每个元素向左移动,但这是疯狂的,而且效率极低。或者,您可以以 MAX
为模递增 front
,允许 rear
领先于 front
,只要您知道最多可以插入 MAX
个元素。这样会更好,但它会破坏removeFromQueue() 中的逻辑,因为当您操作队列时,先前返回的指针可能会指向不同的线程结构 - 彻底的灾难。绝对不是你想要的。
更好的方法是使用链表来实现这一点,在链表中保存一个指向头部的指针和一个指向尾部的指针。看看http://en.wikipedia.org/wiki/Queue_(abstract_data_type)#Queue_implementation
关于C/多线程/段错误/(可能是)线程队列问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25614976/