昨天看CN-Erlounge-IV讲搞的其中一篇<erlang进程模型在C++中的实践>,对用coroutine实现用户态的线程非常感兴趣,于是查下资料
写了个简单的实验框架,其中schedule算法的实现很简单,只是遍历所有可以执行的coroutine.
代码如下:
uthread.h
/*
* brief: 用ucontext实现的用户级线程框架
* author: kenny huang
* date: 2009/10/13
* email: huangweilook@21cn.com
*/
#ifndef _UTHREAD_H
#define _UTHREAD_H
#include <ucontext.h>
#include <stdio.h>
#include <string.h>
#include <list>
#define MAX_UTHREAD 128
typedef int uthread_id;
#define INVAID_ID -1
//用户态线程的当前状态
enum thread_status
{
ACTIVED = 0,//可运行的
BLOCKED,//被阻塞
SLEEP,//主动休眠
DIE,//死死亡
};
class runnable
{
public:
virtual void main_routine() = 0;
};
class Scheduler;
/*
* 用户态线程
*/
class u_thread
{
friend class Scheduler;
private:
u_thread(runnable *rable,unsigned int ssize,uthread_id uid)
:ssize(ssize),_status(BLOCKED),rable(rable),uid(uid)
{
stack = new char[ssize];
ucontext.uc_stack.ss_sp = stack;
ucontext.uc_stack.ss_size = ssize;
getcontext(&ucontext);
}
~u_thread()
{
delete []stack;
}
static void star_routine();
public:
ucontext_t &GetContext()
{
return ucontext;
}
void SetStatus(thread_status _status)
{
this->_status = _status;
}
thread_status GetStatus()
{
return _status;
}
uthread_id GetUid()
{
return uid;
}
//休眠time时间
static void sleep(uthread_id utid,int t);
private:
ucontext_t ucontext;
char *stack;//coroutine使用的栈
unsigned int ssize;//栈的大小
thread_status _status;
runnable *rable;
uthread_id uid;
};
/*
* 任务调度器
*/
class Scheduler
{
//friend void u_thread::star_routine();
friend class u_thread;
public:
static void scheduler_init();
static void schedule();
static uthread_id uthread_create(runnable *rable,unsigned int stacksize);
private:
static u_thread *GetCurrentUThread()
{
if(current == -1)
return NULL;
return threads[current];
}
//休眠time时间
static void sleep(uthread_id utid,int t);
private:
static std::list<u_thread*> activeList;//可运行uthread列表
static std::list<std::pair<u_thread*,time_t> > sleepList;//正在睡眠uthread列表
static char stack[4096];
static ucontext_t ucontext;
static u_thread *threads[MAX_UTHREAD];
static int total_count;
static int current;//在uthread创建时使用的
};
#endif
uthread.cpp
#include "uthread.h"
#include <assert.h>
#include <stdlib.h>
#include <time.h>
ucontext_t Scheduler::ucontext;
char Scheduler::stack[4096];
u_thread *Scheduler::threads[128];
int Scheduler::total_count = 0;
int Scheduler::current = -1;
std::list<u_thread*> Scheduler::activeList;
std::list<std::pair<u_thread*,time_t> > Scheduler::sleepList;
void u_thread::sleep(uthread_id utid,int t)
{
Scheduler::sleep(utid,t);
}
void u_thread::star_routine()
{
u_thread *current_uthread = Scheduler::GetCurrentUThread();
assert(current_uthread);
//回到Scheduler::uthread_create
current_uthread->SetStatus(ACTIVED);
ucontext_t &cur_context = current_uthread->GetContext();
swapcontext(&cur_context,&Scheduler::ucontext);
current_uthread->rable->main_routine();
current_uthread->SetStatus(DIE);
}
void Scheduler::scheduler_init()
{
for(int i = 0; i < MAX_UTHREAD; ++i)
threads[i] = 0;
getcontext(&ucontext);
ucontext.uc_stack.ss_sp = stack;
ucontext.uc_stack.ss_size = sizeof(stack);
ucontext.uc_link = NULL;
makecontext(&ucontext,schedule, 0);
}
void Scheduler::schedule()
{
while(total_count > 0)
{
//首先执行active列表中的uthread
std::list<u_thread*>::iterator it = activeList.begin();
std::list<u_thread*>::iterator end = activeList.end();
for( ; it != end; ++it)
{
if(*it && (*it)->GetStatus() == ACTIVED)
{
ucontext_t &cur_context = (*it)->GetContext();
swapcontext(&ucontext,&cur_context);
uthread_id uid = (*it)->GetUid();
if((*it)->GetStatus() == DIE)
{
printf("%d die/n",uid);
delete threads[uid];
threads[uid] = 0;
--total_count;
activeList.erase(it);
break;
}
else if((*it)->GetStatus() == SLEEP)
{
printf("%d sleep/n",uid);
activeList.erase(it);
break;
}
}
}
//看看Sleep列表中是否有uthread该醒来了
std::list<std::pair<u_thread*,time_t> >::iterator its = sleepList.begin();
std::list<std::pair<u_thread*,time_t> >::iterator ends = sleepList.end();
time_t now = time(NULL);
for( ; its != ends; ++its)
{
//可以醒来了
if(now >= its->second)
{
u_thread *uthread = its->first;
uthread->SetStatus(ACTIVED);
activeList.push_back(uthread);
sleepList.erase(its);
break;
}
}
}
printf("scheduler end/n");
}
uthread_id Scheduler::uthread_create(runnable *rable,unsigned int stacksize)
{
if(total_count >= MAX_UTHREAD)
return INVAID_ID;
int i = 0;
for( ; i < MAX_UTHREAD; ++i)
{
if(threads[i] == 0)
{
threads[i] = new u_thread(rable,stacksize,i);
++total_count;
current = i;
ucontext_t &cur_context = threads[i]->GetContext();
cur_context.uc_link = &ucontext;
makecontext(&cur_context,u_thread::star_routine, 0);
swapcontext(&ucontext, &cur_context);
current = -1;
activeList.push_back(threads[i]);
return i;
}
}
}
void Scheduler::sleep(uthread_id utid,int t)
{
if(utid == INVAID_ID)
return;
assert(threads[utid]);
time_t now = time(NULL);
now += t;
printf("wake up time %u/n",now);
//插入到sleep列表中
sleepList.push_back(std::make_pair(threads[utid],now));
//保存当前上下文切换回scheduler
threads[utid]->SetStatus(SLEEP);
ucontext_t &cur_context = threads[utid]->GetContext();
swapcontext(&cur_context,&Scheduler::ucontext);
}
uthreadtest.cpp
// kcoroutine.cpp : 定义控制台应用程序的入口点。
//
#include "uthread.h"
class runable_test : public runnable
{
public:
runable_test(const char *name):name(name){}
void main_routine()
{
for(int i =0 ; i < 10 ; ++i)
{
printf("%s/n",name);
u_thread::sleep(uid,1);
printf("%s wake up/n",name);
}
}
const char *name;
uthread_id uid;
};
int main()
{
//首先初始化调度器
Scheduler::scheduler_init();
runable_test t1("0");
runable_test t2("1");
runable_test t3("2");
runable_test t4("3");
//创建4个用户级线程
t1.uid = Scheduler::uthread_create(&t1,4096);
t2.uid = Scheduler::uthread_create(&t2,4096);
t3.uid = Scheduler::uthread_create(&t3,4096);
t4.uid = Scheduler::uthread_create(&t4,4096);
printf("create finish/n");
//开始调度线程的运行
Scheduler::schedule();
return 0;
}
在linux下执行
g++ -o test uthreadtest.cpp uthread.cpp
便可生成可执行文件