吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1902|回复: 6
收起左侧

[C&C++ 转载] 模拟线程切换

  [复制链接]
古月不傲 发表于 2020-3-1 16:18
链接:
https://www.bilibili.com/video/av68700135?p=47
http://blog.initm.com/threadswitch.shtml
[C] 纯文本查看 复制代码
//头文件
#include <iostream>
#include <Windows.h>

using namespace std;

#pragma once

//最大支持的线程数
#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 "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;	//-4
	**Stackpp = v;	//push 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 = 0;
	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	//执行GMThreadStartup
	}
}

//这个函数会让出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;
}

//测试
#include "ThreadSwitch.h"

extern int CurrentThreadIndex;

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

void Thread3(void*) {
	while (1) {
		printf("Thread3\n");
		GMSleep(300);
	}
}

void Thread4(void*) {
	while (1) {
		printf("Thread4\n");
		GMSleep(200);
	}
}


int main()
{
	char name1[20] = "Thread1";
	char name2[20] = "Thread2";
	char name3[20] = "Thread3";
	char name4[20] = "Thread4";
	RegisterGMThread(name1, Thread1, NULL);
	RegisterGMThread(name2, Thread2, NULL);
	RegisterGMThread(name3, Thread3, NULL);
	RegisterGMThread(name4, Thread4, NULL);

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

	return 0;
}

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
苏紫方璇 + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
那年听风 发表于 2020-3-1 16:31
提示: 作者被禁止或删除 内容自动屏蔽
此用户无法显示 发表于 2020-3-1 16:33
yywapj 发表于 2020-3-1 17:18
xlxjyxs 发表于 2021-11-25 20:22
支持一下
kilasuelika 发表于 2021-11-27 10:06
厉害厉害
zoiitylj 发表于 2021-11-27 15:55
牛逼,切换线程是操作系统的事,能自己切换不是可以给特定线程多安排时间片了?GetTickCount的粒度不如timegettime
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 11:59

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表