(23)[进程与线程] 模拟线程代码中添加挂起和恢复功能

文章目录


上一次完成了线程模拟的代码 (上一次的代码),在那文章的最后,线程模拟

新增

完整代码见最后

新增一个宏定义,用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;
}

可以对比 原来的代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值