上一次完成了线程模拟的代码 (上一次的代码),在那文章的最后,线程模拟
新增
完整代码见最后
新增一个宏定义,用flags来表示
#define GMTHREAD_SUSPEND 0x10
当GMTHREAD_SUSPEND
为1,代表线程挂起
并在Scheduling()
搜索就绪状态线程的循环中添加代码判断是否为挂起
if (GMThreadList[i].Flags & GMTHREAD_SUSPEND) {
continue; //被挂起则直接下一轮循环
}
scheduling每次从1开始搜索,意味着后面的进程可能会饿死
所以可以稍微优化一下循环,每次从当前索引开始
可以通过if (i == 0) continue跳过调度线程,写不写都没啥影响。不写,调度线程能有更多执行机会
for (int i = CurrentThreadIndex+1;
GMThreadList[i].name && i != CurrentThreadIndex;
i = (i+1) % GMThread_max_size)
新增三个挂起函数
void GMThreadSuspend(); //自己挂起自己
void GMThreadSuspendEx(GMThread_t* pGMThread);//挂起别人
void GMThreadResume(GMThread_t* pGMThread);//帮助别人恢复(因为自己被挂起了,不能自救)
实现也非常的简单,就不写出来了,在完整代码里有标注 所有新增内容
测试函数Thread_x能用求余运算写成一个,但是不好看。拆成三个,逻辑更加清晰
测试逻辑:创建3个线程 1 2 3,先挂起2 3 (恢复未挂起进程也不会有任何问题,但是逻辑上不太好)
1:打印 1 后,恢复 线程2,并挂起自身
2:打印 2 后,恢复 线程3,并挂起自身
3:打印 3 后,恢复 线程1,并挂起自身
打印结果,123 123 123 …
完整代码
//----------------------------------------thread.h----------------------------------------
#pragma once
#pragma warning(disable: 4996)
#include <Windows.h>
#include <stdio.h>
#if 1
#define GMTHREAD_CREATE 0x01
#define GMTHREAD_READY 0x02
#define GMTHREAD_RUNNING 0x04
#define GMTHREAD_SLEEP 0x08
#define GMTHREAD_SUSPEND 0x10 //新增
#define GMTHREAD_EXIT 0x100
#else
//如果不是C代码的话,可以用这个
enum { //其实写成小写比较好看,但这么写能通用
GMTHREAD_CREATE = 0x01,
GMTHREAD_READY = 0x02,
GMTHREAD_RUNNING = 0x04,
GMTHREAD_SLEEP = 0x08,
GMTHREAD_EXIT = 0x100
};
#endif
typedef unsigned char UCHAR;
typedef unsigned int UINT;
typedef void (*callbackFunc)(void*);
typedef struct {
char* name; //线程名,相当于线程TID
int Flags; //线程状态
int SleepMillisecondDot; //休眠时间
void* InitalStack; //线程堆栈起始位置
void* StackLimit; //线程堆栈界限
void* KernelStack; //线程堆栈当前位置 (EPS)
void* arg; //线程函数的参数
callbackFunc func; //线程函数
}GMThread_t;
extern const size_t GMThread_max_size;
extern const size_t GMThread_stack_size;
extern const char* _self;
extern int CurrentThreadIndex;
extern void* WindowsStackLimit;
extern GMThread_t GMThreadList[];
void InitGMThread(GMThread_t *pGMTHread, char* name, callbackFunc func, void* arg);
int RegisterGMThread(char* name, callbackFunc func, void* arg);
void PushStack(UINT** stack, UINT data);
void GMThreadStartup(GMThread_t* pGMThread);
void Scheduling();
void SwitchContext(GMThread_t* OldGMThreadp, GMThread_t* NewGMThreadp);
void IdleGMThread(void* arg);
void GMSleep(int Milliseconds);
void coroutine_test();
//新增函数
void GMThreadSuspend();
void GMThreadSuspendEx(GMThread_t* pGMThread);
void GMThreadResume(GMThread_t* pGMThread);
//貌似这里传GMThread_t*这个不太合理
//但这个是实验性质的,这么写简单
//----------------------------------------thread.cpp----------------------------------------
#include "coroutine.h"
const size_t GMThread_max_size = 12;
const size_t GMThread_stack_size = 0x80000;
const char* _self = "仿windows线程切换";
int CurrentThreadIndex = 0;
void* WindowsStackLimit = NULL;
GMThread_t GMThreadList[GMThread_max_size] = { 0 };
void InitGMThread(GMThread_t* pGMThread, char* name, callbackFunc func, void* arg) {
UCHAR* StackPages;
UINT* t_ESP;
pGMThread->Flags = GMTHREAD_CREATE;
pGMThread->name = name;
pGMThread->func = func;
pGMThread->arg = arg;
//申请空间
StackPages = (UCHAR*)VirtualAlloc(NULL, GMThread_stack_size, MEM_COMMIT, PAGE_READWRITE);
//StackPages置零
memset(StackPages, 0, GMThread_stack_size);
//堆栈初始化地址
pGMThread->InitalStack = StackPages + GMThread_stack_size;
//堆栈限制
pGMThread->StackLimit = StackPages;
//堆栈地址
t_ESP = (UINT*)pGMThread->InitalStack;
PushStack(&t_ESP, (UINT)pGMThread); //通过这个指针来找到:线程函数,函数参数
PushStack(&t_ESP, (UINT)9); //平衡堆栈,无意义
PushStack(&t_ESP, (UINT)GMThreadStartup); //函数入口 eip
PushStack(&t_ESP, 5); //ebp,无意义(下同)
PushStack(&t_ESP, 7); //edi
PushStack(&t_ESP, 6); //esi
PushStack(&t_ESP, 3); //ebx
PushStack(&t_ESP, 2); //ecx
PushStack(&t_ESP, 1); //edx
PushStack(&t_ESP, 0); //eax
pGMThread->KernelStack = t_ESP;
pGMThread->Flags = GMTHREAD_READY;
return;
}
int RegisterGMThread(char* name, callbackFunc func, void* arg) {
int i;
for (i = 1; GMThreadList[i].name; i++) {
if (stricmp(GMThreadList[i].name, name) == 0) {
break;
}
}
InitGMThread(&GMThreadList[i], name, func, arg);
return (i | 0x55AA0000);
}
void PushStack(UINT** stack, UINT data) {
*stack -= 1;
**stack = data;
return;
}
void GMThreadStartup(GMThread_t* pGMThread) {
pGMThread->func(pGMThread->arg);
pGMThread->Flags = GMTHREAD_EXIT;
Scheduling();
return;
}
//如果存在ready的线程,那么就继续执行ready线程,如果没有返回到调度线程进行调度
void Scheduling() {
int tickCount = GetTickCount();
GMThread_t* pOldGMThread = &GMThreadList[CurrentThreadIndex];
GMThread_t* pNewGMThread = &GMThreadList[0];
//for (int i = 1; GMThreadList[i].name; i++) { //修改
for (int i = CurrentThreadIndex+1; i != CurrentThreadIndex; i = (i+1) % GMThread_max_size) {
if (!GMThreadList[i].name) continue;
if (GMThreadList[i].Flags & GMTHREAD_SLEEP) {
//sleep结束?
if (tickCount > GMThreadList[i].SleepMillisecondDot) {
GMThreadList[i].Flags = GMTHREAD_READY;
}
}
//挂起了,跳过 (新增)
if (GMThreadList[i].Flags & GMTHREAD_SUSPEND) {
continue;
}
//找到ready线程,退出循环
if (GMThreadList[i].Flags & GMTHREAD_READY) {
pNewGMThread = &GMThreadList[i];
break;
}
}
CurrentThreadIndex = pNewGMThread - GMThreadList;
SwitchContext(pOldGMThread, pNewGMThread);
}
__declspec(naked) void SwitchContext(GMThread_t* OldGMThreadp, GMThread_t* NewGMThreadp) {
_asm {
//保存当前线程 现场
push ebp
mov ebp, esp
push edi
push esi
push ebp
push ecx
push edx
push eax
mov esi, OldGMThreadp
mov edi, NewGMThreadp
mov [esi + GMThread_t.KernelStack], esp
//-------------------------切换堆栈-----------------------
mov esp, [edi + GMThread_t.KernelStack]
//还原新线程 现场
pop eax
pop edx
pop ecx
pop ebx
pop esi
pop edi
pop ebp
ret //pop eip
}
}
void IdleGMThread(void* arg) {
printf("Idle GMtrhead...\n");
Scheduling();
return;
}
void GMSleep(int Milliseconds) {
GMThread_t* GMThreadp;
GMThreadp = &GMThreadList[CurrentThreadIndex];
if ((GMThreadp->Flags) != 0)
{
GMThreadp->SleepMillisecondDot = GetTickCount() + Milliseconds;
GMThreadp->Flags = GMTHREAD_SLEEP;
}
Scheduling();
return;
}
//自己挂起 (新增)
void GMThreadSuspend() {
GMThreadList[CurrentThreadIndex].Flags |= GMTHREAD_SUSPEND;
Scheduling();
}
//别人挂起 (新增)
void GMThreadSuspendEx(GMThread_t* pGMThread) {
pGMThread->Flags |= GMTHREAD_SUSPEND;
Scheduling();
}
//由于自己不能给自己结束挂起,只能由其他线程决定什么时候唤醒 (新增)
void GMThreadResume(GMThread_t* pGMThread) {
pGMThread->Flags &= ~GMTHREAD_SUSPEND;
}
//-------------------------------test------------------------------------
//内容有所修改
void Thread_1(void* arg) {
int i = (int)arg;
while (1) {
printf("thrad: %d\n", i);
GMThreadResume(&GMThreadList[2]);
GMThreadSuspend();
}
return;
}
void Thread_2(void* arg) {
int i = (int)arg;
while (1) {
printf("thrad: %d\n", i);
GMThreadResume(&GMThreadList[3]);
GMThreadSuspend();
}
return;
}
void Thread_3(void* arg) {
int i = (int)arg;
while (1) {
printf("thrad: %d\n", i);
GMThreadResume(&GMThreadList[1]);
GMThreadSuspend();
}
return;
}
void coroutine_test() {
char name_1[] = "Thread1";
char name_2[] = "Thread2";
char name_3[] = "Thread3";
RegisterGMThread(name_1, Thread_1, (void*)1);
RegisterGMThread(name_2, Thread_2, (void*)2);
RegisterGMThread(name_3, Thread_3, (void*)3);
GMThreadSuspendEx(&GMThreadList[2]);
GMThreadSuspendEx(&GMThreadList[3]);
for (;;) {
Sleep(200);
Scheduling();
}
return;
}
可以对比 原来的代码