[ThreadSwitch]模拟windows线程切换

2018年5月15日 133点热度 0人点赞 0条评论

这段代码出自滴水的课程,我只是自己写了一遍,留下来以后可以看一下。感谢海东老师。

#pragma once
#include <windows.h>
//最大支持的线程数
#define MAXGMTHREAD 100

//线程信息的结构
typedef struct 
{
    char* name;                     //线程名
    int Flags;                      //线程状态
    int SleepMillsecondDot;         //休眠时间

    void* initialStack;             //线程堆栈起始位置
    void* StackLimit;               //线程堆栈界限
    void* KernelStack;              //线程堆栈当前位置,也就是ESP

    void* lpParameter;              //线程函数的参数
    void(*func)(void* lpParameter); //线程函数
}GMThread_t;

void GMSleep(int MilliSeconds);
int RegisterGMThread(char* name, void(*func)(void*lpParameter), void* lpParameter);
void Scheduling(void);

#include stdafx.h
#include ThreadSwitch.h

//定义线程栈的大小
#define GMTHREADSTACKSIZE 0x80000

//当前线程的索引
int CurrentThreadIndex = 0;

//线程的列表
GMThread_t GMThreadList[MAXGMTHREAD] = {NULL, 0};

//线程状态的标志
enum FLAGS
{
    GMTHREAD_CREATE = 0x1,
    GMTHREAD_READY = 0x2,
    GMTHREAD_SLEEP = 0x4,
    GMTHREAD_EXIT = 0x8,
};

//启动线程的函数
void GMThreadStartup(GMThread_t* GMThreadp)
{
    GMThreadp->func(GMThreadp->lpParameter);
    GMThreadp->Flags = GMTHREAD_EXIT;
    Scheduling();

    return;
}

//空闲线程的函数
void IdleGMThread(void* lpParameter)
{
    printf(IdleGMThread---------------\n);
    Scheduling();
    return;
}

//向栈中压入一个uint值
void PushStack(unsigned int** Stackpp, unsigned int v) 
{
    *Stackpp -= 1;
    **Stackpp = v;

    return;
}

//初始化线程的信息
void initGMThread(GMThread_t* GMThreadp, char* name, void (*func)(void* lpParameter), void* lpParameter)
{
    unsigned char* StackPages;
    unsigned int* StackDWordParam;
    GMThreadp->Flags = GMTHREAD_CREATE;
    GMThreadp->name = name;
    GMThreadp->func = func;
    GMThreadp->lpParameter = lpParameter;
    StackPages = (unsigned char*)VirtualAlloc(NULL, GMTHREADSTACKSIZE, MEM_COMMIT, PAGE_READWRITE);
    ZeroMemory(StackPages, GMTHREADSTACKSIZE);
    GMThreadp->initialStack = StackPages + GMTHREADSTACKSIZE;
    StackDWordParam = (unsigned int*)GMThreadp->initialStack;
    //入栈
    PushStack(&StackDWordParam, (unsigned int)GMThreadp);
    PushStack(&StackDWordParam, (unsigned int)0);
    PushStack(&StackDWordParam, (unsigned int)GMThreadStartup);
    PushStack(&StackDWordParam, (unsigned int)5);
    PushStack(&StackDWordParam, (unsigned int)7);
    PushStack(&StackDWordParam, (unsigned int)6);
    PushStack(&StackDWordParam, (unsigned int)3);
    PushStack(&StackDWordParam, (unsigned int)2);
    PushStack(&StackDWordParam, (unsigned int)1);
    PushStack(&StackDWordParam, (unsigned int)0);
    //当前线程的栈顶
    GMThreadp->KernelStack = StackDWordParam;
    GMThreadp->Flags = GMTHREAD_READY;
    return;
}

//将一个函数注册为单独线程执行
int RegisterGMThread(char* name, void(*func)(void*lpParameter), void* lpParameter)
{
    int i;
    for (i = 1; GMThreadList[i].name; i++) {
        if (0 == _stricmp(GMThreadList[i].name, name)) {
            break;
        }
    }
    initGMThread(&GMThreadList[i], name, func, lpParameter);

    return (i & 0x55AA0000);
}

//切换线程
__declspec(naked) void SwitchContext(GMThread_t* SrcGMThreadp, GMThread_t* DstGMThreadp)
{
    __asm {
        push ebp
        mov ebp, esp
        push edi
        push esi
        push ebx
        push ecx
        push edx
        push eax

        mov esi, SrcGMThreadp
        mov edi, DstGMThreadp
        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
    }
}

//这个函数会让出cpu,从队列里重新选择一个线程执行
void Scheduling(void)
{
    int i;
    int TickCount;
    GMThread_t* SrcGMThreadp;
    GMThread_t* DstGMThreadp;
    TickCount = GetTickCount();
    SrcGMThreadp = &GMThreadList[CurrentThreadIndex];
    DstGMThreadp = &GMThreadList[0];

    for (i = 1; GMThreadList[i].name; i++) {
        if (GMThreadList[i].Flags & GMTHREAD_SLEEP) {
            if (TickCount > GMThreadList[i].SleepMillsecondDot) {
                GMThreadList[i].Flags = GMTHREAD_READY;
            }
        }
        if (GMThreadList[i].Flags & GMTHREAD_READY) {
            DstGMThreadp = &GMThreadList[i];
            break;
        }
    }

    CurrentThreadIndex = DstGMThreadp - GMThreadList;
    SwitchContext(SrcGMThreadp, DstGMThreadp);
    return;
}

void GMSleep(int MilliSeconds)
{
    GMThread_t* GMThreadp;
    GMThreadp = &GMThreadList[CurrentThreadIndex];
    if (GMThreadp->Flags != 0) {
        GMThreadp->Flags = GMTHREAD_SLEEP;
        GMThreadp->SleepMillsecondDot = GetTickCount() + MilliSeconds;
    }

    Scheduling();
    return;
}

<pre lang=C++ title=main.cpp>#include stdafx.h
#include ThreadSwitch.h

extern int CurrentThreadIndex;

extern GMThread_t GMThreadList[MAXGMTHREAD];
void Thread1(void*) {
    while(1){
        printf(Thread1);
        GMSleep(500);
    }
}
void Thread2(void*) {
    while (1) {
        printf(Thread2);
        GMSleep(200);
    }
}

void Thread3(void*) {
    while (1) {
        printf(Thread3);
        GMSleep(10);
    }
}

void Thread4(void*) {
    while (1) {
        printf(Thread4);
        GMSleep(1000);
    }
}

int main()
{
    RegisterGMThread(Thread1, Thread1, NULL);
    RegisterGMThread(Thread2, Thread2, NULL);
    RegisterGMThread(Thread3, Thread3, NULL);
    RegisterGMThread(Thread4, Thread4, NULL);

    while(TRUE) {
        Sleep(20);
        Scheduling();
    }

    return 0;
}

Stupid

一个人用自己的船将他人送到彼岸,那他自己也到达了彼岸。

文章评论