
通过多进程来实现,用这两个命令:
调用格式: 〈逻辑型〉 启动线程 (子程序指针 欲执行的子程序,[整数型 参数数据],[整数型变量 线程句柄]) - 多线程支持库->多线程控制
英文名称:CreateThread
创建并启动一条线程,可重复使用以创建多条线程,成功返回真,失败返回假。本命令为初级命令。
参数<1>的名称为“欲执行的子程序”,类型为“子程序指针(SubPtr)”。本参数提供创建线程时欲启动的子程序,根据是否需要传递参数数据,该子程序必须没有或具有一个整数型参数,否则将出错。
参数<2>的名称为“参数数据”,类型为“整数型(int)”,可以被省略。本参数提供线程被启动时传递到欲启动子程序的整数数据。如果本参数未被省略,此时被启动子程序必须接收一个整数型参数,否则将出错。
参数<3>的名称为“线程句柄”,类型为“整数型(int)”,可以被省略,提供参数数据时只能提供变量。如果提供了本参数,将向参数变量写入线程句柄(Windows下为HANDLE,Linux下为pthread_t),请在适当的时机关闭该句柄。如果不提供本参数接收线程句柄,内部将自动处理线程句柄。
*** 作系统需求: Windows、Linux
————————————————————————————————————————
调用格式: 〈逻辑型〉 关闭线程句柄 (整数型 线程句柄) - 多线程支持库->多线程控制
英文名称:CloseThreadHandle
返回真表示已成功关闭线程句柄。在Linux下,如果线程已经结束,本命令可能返回假。本命令为初级命令。
参数<1>的名称为“线程句柄”,类型为“整数型(int)”。可通过“启动线程”的第三个参数获取线程句柄。
*** 作系统需求: Windows、Linux
======================================================================
按F1关闭线程,要注册热键:
调用格式: 〈整数型〉 注册热键 (整数型 窗口句柄,整数型 标签句柄,整数型 功能键,整数型 主热键) - 扩展功能支持库一->热键功能
英文名称:RegHotKey
注册系统热键,返回一个热键标识,失败返回0。本命令为初级命令。
参数<1>的名称为“窗口句柄”,类型为“整数型(int)”。窗口句柄。
参数<2>的名称为“标签句柄”,类型为“整数型(int)”。使用标签的反馈事件来接受热键,反馈事件中的第一个参数为热键标识,第二个参数无效。
参数<3>的名称为“功能键”,类型为“整数型(int)”,初始值为“0”。可以为:0-无功能键;1-CTRL键状态;2-SHIFT键状态;4-ALT键状态或各键状态值之和。
参数<4>的名称为“主热键”,类型为“整数型(int)”。键代码,可以使用易语言中的键代码常量。
*** 作系统需求: Windows
1,一个进程至少拥有一个线程,称为主线程,如果一个线程创建了窗口,拥有GUI资源,那么也称该线程为 GUI线程 ,否则就为 工作线程 。窗口是由线程创建的,创建窗口的线程就拥有该窗口。这种线程拥有关系的概念对窗口有重要的意义:建立窗口的线程必须是为窗口处理所有消息的线程。为了使这个概念更加明确具体,可以想像一个线程建立了一个窗口,然后就结束了。
在这种情况下,窗口不会收到一个WM_DESTROY或WM_NCDESTROY消息,因为线程已经结束,不可能被用来使窗口接收和处理这些消息。每个线程,如果它至少建立了一个窗口,都由系统对它分配一个消息队列。这个队列用于窗口消息的派送(dispatch)。为了使窗口接收这些消息,线程必须有它自己的消息循环,消息循环一般如下:
应用程序不断的从消息队列中获取消息,然后系统通过DispatchMessage函数分派消息到相应窗口的窗口过程,使得消息得到处理。当获取到WM_QUIT消息时,GetMessage返回0,循环结束。
进程是可并发执行的程序在一个数据集合上的运行过程,它是申请和拥有资源的基本单位。而线程是进程中的一个实体,是独立调度和分派的基本单位,线程自身基本上不拥有系统资源,只拥有少许运行中必不可少的私有资源。线程可与同属一个进程的其他线程共享进程的全部资源。现代 *** 作系统和软件设计大多支持多线程运行。Windows系列 *** 作系统采用了多进程,多线程的技术。在Windows环境下,当运行一个程序时,就创建了一个用来容纳这个程序的可执行文件的代码即其所需调用的动态链接库的进程。每个进程都运行在它专用的 受保护的地址空间内。进程分系统进程和用户进程,凡是用于完成 *** 作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的 *** 作系统本身;用户进程就是所有由你启动的进程。
#include<windowsh>
#include<fstreamh>
#include<stdioh>
#include<string>
#include<conioh>
//定义一些常量;
//本程序允许的最大临界区数;
#define MAX_BUFFER_NUM 10
//秒到微秒的乘法因子;
#define INTE_PER_SEC 1000
//本程序允许的生产和消费线程的总数;
#define MAX_THREAD_NUM 64
//定义一个结构,记录在测试文件中指定的每一个线程的参数
struct ThreadInfo
{
int serial; //线程序列号
char entity; //是P还是C
double delay; //线程延迟
int thread_request[MAX_THREAD_NUM]; //线程请求队列
int n_request; //请求个数
};
//全局变量的定义
//临界区对象的声明,用于管理缓冲区的互斥访问;
CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM];
int Buffer_Critical[MAX_BUFFER_NUM]; //缓冲区声明,用于存放产品;
HANDLE h_Thread[MAX_THREAD_NUM]; //用于存储每个线程句柄的数组;
ThreadInfo Thread_Info[MAX_THREAD_NUM]; //线程信息数组;
HANDLE empty_semaphore; //一个信号量;
HANDLE h_mutex; //一个互斥量;
DWORD n_Thread = 0; //实际的线程的数目;
DWORD n_Buffer_or_Critical; //实际的缓冲区或者临界区的数目;
HANDLE h_Semaphore[MAX_THREAD_NUM]; //生产者允许消费者开始消费的信号量;
//生产消费及辅助函数的声明
void Produce(void p);
void Consume(void p);
bool IfInOtherRequest(int);
int FindProducePositon();
int FindBufferPosition(int);
int main(void)
{
//声明所需变量;
DWORD wait_for_all;
ifstream inFile;
//初始化缓冲区;
for(int i=0;i< MAX_BUFFER_NUM;i++)
Buffer_Critical[i] = -1;
//初始化每个线程的请求队列;
for(int j=0;j<MAX_THREAD_NUM;j++){
for(int k=0;k<MAX_THREAD_NUM;k++)
Thread_Info[j]thread_request[k] = -1;
Thread_Info[j]n_request = 0;
}
//初始化临界区;
for(i =0;i< MAX_BUFFER_NUM;i++)
InitializeCriticalSection(&PC_Critical[i]);
//打开输入文件,按照规定的格式提取线程等信息;
inFileopen("testtxt");
//从文件中获得实际的缓冲区的数目;
inFile >> n_Buffer_or_Critical;
inFileget();
printf("输入文件是:\n");
//回显获得的缓冲区的数目信息;
printf("%d \n",(int) n_Buffer_or_Critical);
//提取每个线程的信息到相应数据结构中;
while(inFile){
inFile >> Thread_Info[n_Thread]serial;
inFile >> Thread_Info[n_Thread]entity;
inFile >> Thread_Info[n_Thread]delay;
char c;
inFileget(c);
while(c!='\n'&& !inFileeof()){
inFile>> Thread_Info[n_Thread]thread_request[Thread_Info[n_Thread]n_request++];
inFileget(c);
}
n_Thread++;
}
//回显获得的线程信息,便于确认正确性;
for(j=0;j<(int) n_Thread;j++){
int Temp_serial = Thread_Info[j]serial;
char Temp_entity = Thread_Info[j]entity;
double Temp_delay = Thread_Info[j]delay;
printf(" \n thread%2d %c %f ",Temp_serial,Temp_entity,Temp_delay);
int Temp_request = Thread_Info[j]n_request;
for(int k=0;k<Temp_request;k++)
printf(" %d ", Thread_Info[j]thread_request[k]);
cout<<endl;
}
printf("\n\n");
//创建在模拟过程中几个必要的信号量
empty_semaphore=CreateSemaphore(NULL,n_Buffer_or_Critical,n_Buffer_or_Critical,
"semaphore_for_empty");
h_mutex = CreateMutex(NULL,FALSE,"mutex_for_update");
//下面这个循环用线程的ID号来为相应生产线程的产品读写时所
//使用的同步信号量命名;
for(j=0;j<(int)n_Thread;j++){
std::string lp ="semaphore_for_produce_";
int temp =j;
while(temp){
char c = (char)(temp%10);
lp+=c;
temp/=10;
}
h_Semaphore[j+1]=CreateSemaphore(NULL,0,n_Thread,lpc_str());
}
//创建生产者和消费者线程;
for(i =0;i< (int) n_Thread;i++){
if(Thread_Info[i]entity =='P')
h_Thread[i]= CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce),
&(Thread_Info[i]),0,NULL);
else
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consume),
&(Thread_Info[i]),0,NULL);
}
//主程序等待各个线程的动作结束;
wait_for_all = WaitForMultipleObjects(n_Thread,h_Thread,TRUE,-1);
printf(" \n \nALL Producer and consumer have finished their work \n");
printf("Press any key to quit!\n");
_getch();
return 0;
}
//确认是否还有对同一产品的消费请求未执行;
bool IfInOtherRequest(int req)
{
for(int i=0;i<n_Thread;i++)
for(int j=0;j<Thread_Info[i]n_request;j++)
if(Thread_Info[i]thread_request[j] == req)
return TRUE;
return FALSE;
}
//找出当前可以进行产品生产的空缓冲区位置;
int FindProducePosition()
{
int EmptyPosition;
for (int i =0;i<n_Buffer_or_Critical;i++)
if(Buffer_Critical[i] == -1){
EmptyPosition = i;
//用下面这个特殊值表示本缓冲区正处于被写状态;
Buffer_Critical[i] = -2;
break;
}
return EmptyPosition;
}
//找出当前所需生产者生产的产品的位置;
int FindBufferPosition(int ProPos)
{
int TempPos;
for (int i =0 ;i<n_Buffer_or_Critical;i++)
if(Buffer_Critical[i]==ProPos){
TempPos = i;
break;
}
return TempPos;
}
//生产者进程
void Produce(void p)
{
//局部变量声明;
DWORD wait_for_semaphore,wait_for_mutex,m_delay;
int m_serial;
//获得本线程的信息;
m_serial = ((ThreadInfo)(p))->serial;
m_delay = (DWORD)(((ThreadInfo)(p))->delay INTE_PER_SEC);
Sleep(m_delay);
//开始请求生产
printf("Producer %2d sends the produce require\n",m_serial);
//确认有空缓冲区可供生产,同时将空位置数empty减1;用于生产者和消费者的同步;
wait_for_semaphore = WaitForSingleObject(empty_semaphore,-1);
//互斥访问下一个可用于生产的空临界区,实现写写互斥;
wait_for_mutex = WaitForSingleObject(h_mutex,-1);
int ProducePos = FindProducePosition();
ReleaseMutex(h_mutex);
//生产者在获得自己的空位置并做上标记后,以下的写 *** 作在生产者之间可以并发;
//核心生产步骤中,程序将生产者的ID作为产品编号放入,方便消费者识别;
printf("Producer %2d begin to produce at position %2d\n",m_serial,ProducePos);
Buffer_Critical[ProducePos] = m_serial;
printf("Producer %2d finish producing :\n ",m_serial);
printf(" position[ %2d ]:%3d \n" ,ProducePos,Buffer_Critical[ProducePos]);
//使生产者写的缓冲区可以被多个消费者使用,实现读写同步;
ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL);
}
//消费者进程
void Consume(void p)
{
//局部变量声明;
DWORD wait_for_semaphore,m_delay;
int m_serial,m_requestNum; //消费者的序列号和请求的数目;
int m_thread_request[MAX_THREAD_NUM];//本消费线程的请求队列;
//提取本线程的信息到本地;
m_serial = ((ThreadInfo)(p))->serial;
m_delay = (DWORD)(((ThreadInfo)(p))->delay INTE_PER_SEC);
m_requestNum = ((ThreadInfo )(p))->n_request;
for (int i = 0;i<m_requestNum;i++)
m_thread_request[i] = ((ThreadInfo)(p))->thread_request[i];
Sleep(m_delay);
//循环进行所需产品的消费
for(i =0;i<m_requestNum;i++){
//请求消费下一个产品
printf("Consumer %2d request to consume %2d product\n",m_serial,m_thread_request[i]);
//如果对应生产者没有生产,则等待;如果生产了,允许的消费者数目-1;实现了读写同步;
wait_for_semaphore=WaitForSingleObject(h_Semaphore[m_thread_request[i]],-1);
//查询所需产品放到缓冲区的号
int BufferPos=FindBufferPosition(m_thread_request[i]);
//开始进行具体缓冲区的消费处理,读和读在该缓冲区上仍然是互斥的;
//进入临界区后执行消费动作;并在完成此次请求后,通知另外的消费者本处请求已
//经满足;同时如果对应的产品使用完毕,就做相应处理;并给出相应动作的界面提
//示;该相应处理指将相应缓冲区清空,并增加代表空缓冲区的信号量;
EnterCriticalSection(&PC_Critical[BufferPos]);
printf("Consumer%2d begin to consume %2d product \n",m_serial,m_thread_request[i]);
((ThreadInfo)(p))->thread_request[i] =-1;
if(!IfInOtherRequest(m_thread_request[i])){
Buffer_Critical[BufferPos] = -1;//标记缓冲区为空;
printf("Consumer%2d finish consuming %2d:\n ",m_serial,m_thread_request[i]);
printf(" position[ %2d ]:%3d \n" ,BufferPos,Buffer_Critical[BufferPos]);
ReleaseSemaphore(empty_semaphore,1,NULL);
}
else{
printf("Consumer %2d finish consuming product %2d\n ",m_serial,m_thread_request[i]);
}
//离开临界区
LeaveCriticalSection(&PC_Critical[BufferPos]);
}
}
看函数就行
法。
1 引言
本文分析了Windows 系统的进程调度机制,并设计了一种基于Windows *** 作系统内核驱动的多核CPU 线程管理方法,实现了一个基于Windows 内核驱动的线程管理服务系统,它能让用户根据每一个任务线程对CPU 资源的需要程度和对实时性的要求,在多核CPU上合理为线程分配CPU 核。
Windows 内核调度结构体关系图
图1 Windows 内核调度结构体关系图
2 Windows 系统的进程调度方法分析
Windows NT 中的每一个进程都是EPROCESS 结构体。此结构体中除了进程的属性之外还引用了其它一些与实现进程紧密相关的结构体。例如,每个进程都有一个或几个线程,线程在系统中就是ETHREAD 结构体。简要描述一下存在于这个结构体中的主要的信息,这些信息都是由对内核函数的研究而得知的。首先,结构体中有KPROCESS 结构体,这个结构体中又有指向这些进程的内核线程(KTHREAD)链表的指针(分配地址空间),基优先级,在内核模式或是用户模式执行进程的线程的时间,处理器affini ty(掩码,定义了哪个处理器能执行进程的线程),时间片值。在ETHREAD 结构体中还存在着这样的信息:进程ID、父进程ID、进程映象名。
在E P R O C E S S 结构体中还有指向P E B 的指针。
ETHREAD 结构体还包含有创建时间和退出时间、进程ID 和指向EPROCESS 的指针,启动地址,I/O 请求链表和KTHREAD 结构体。在KTHREAD 中包含有以下信息:内核模式和用户模式线程的创建时间,指向内核堆栈基址和顶点的指针、指向服务表的指针、基优先级与当前优先级、指向APC 的指针和指向T E B 的指针。
KTHREAD 中包含有许多其它的数据,通过观察这些数据可以分析出KTHREAD 的结构。图1 描述了这些结构体之间的关系。
通过遍历KPROCESS 结构体中的ETHREAD,找到系统中当前所有的KTHREAD 结构,这个结构中的偏移量为0x124 处的Affinity 域(Windows XP sp3)即为设置CPU 亲缘性掩码的内存地址。在此重点解释CPU 亲缘性的概念,CPU 亲缘性就是指在系统中能够将一个或多个进程或线程绑定到一个或多个处理器上运行,这是期待已久的特性。也就是说:“ 在1号处理器上一直运行该程序”或者是“在所有的处理器上运行这些程序,而不是在0 号处理器上运行”。然后, 调度器将遵循该规则,程序仅仅运行在允许的处理器上。在Windows *** 作系统上,给程序员设定CPU 亲缘性的接口是用一个32 位的双字型数表示的, 它被称为亲缘性掩码(Affinity bitMask)。亲缘性掩码是一系列的二进制位,每一位代表一个CPU 单元是否可执行当前任务。例如一个在具有四个CPU 的PC 机上( 或四核CPU) ,亲缘性掩码的形式的二进制数如下式所示:
0000000000000000000000000000XXXXB
其中自右向左,每一位代表0 到31 号CPU是否可用,由于本机只有四个CPU, 所以只有前四个位可用,X 为1则代表当前任务可执行在此位代表的CPU 上,X 为0 则代表当前任务不可执行在此位代表的CPU 上, 例如:
00000000000000000000000000000010B
代表当前任务只能执行在1 号 CPU 上(CPU 下标记数从0 开始),又如0x00000004 代表当前任务只能执行在2 号CPU 上,0x00000003 代表当前任务可以运行在0号和1 号CPU 上。
Windows 的进程调度代码是在它的System 进程下的,所以它不属于任何用户进程上下文。调度代码在适当的时机会切换进程上下文,这里的切换进程上下文是指进程环境的切换, 包括内存中的可执行程序, 提供程序运行的各种资源进程拥有虚拟的地址空间,可执行代码, 数据, 对象句柄集, 环境变量, 基础优先级, 以及最大最小工作集等的切换。而Windows 最小的调度单位是线程, 只有线程才是真正的执行体,进程只是线程的容器。Windows 的调度程序在时间片到期,或有切换线程指令执行(如Sleep,KeWaitForSingleObject 等函数)时, 将会从进程线程队列中找到下一个要调度的线程执行体,并装入到KPCR(Kernel ' s Processor Contr ol Re g i o n , 内核进程控制区域) 结构中,CPU 根据KPCR 结构中的KPRCB 结构执行线程执行体代码。而在多核CPU 下,当Windows 调度代码执行时,从当前要调度执行的KTHREAD 结构中取出Affinity,并与当前PC 机上的硬件配置数据中的CPU 掩码作与 *** 作,结果写入到指定的CPU,例如双核CPU 的设备掩码为0x03,如果当前KTHREAD 里的Affinity 为0x01,那么0x01&0x03=0x01,这样执行体线程会被装入CPU1的KPRCB 结构中得以执行,调度程序不会把这个线程交给CPU2 去执行。此过程如图2 所示。这就是为线程选择指定CPU 核的原理。
Windows 内核亲缘性调度原理图
图 2 Windows 内核亲缘性调度原理图。
那么控制线程在指定CPU 上运行的突破口就是修改Windows 内核结构体KTHREAD 下的Affinity 域。然而Windows 内核结构被放在虚拟内存线性地址的高2G(不同版本Windows 下也可能是1G)地址空间,用户模式下的应用程序是无法访问这段内存空间的,所以必须编写Windows 驱动程序,来访问Windows 内核内存空间, 这也是本文将要描述的重点。
3 线程管理服务系统
整个系统的结构如图3 所示。该系统由两大部分组成,分别是内核模式下的管理服务系统设备驱动程序,和用户模式下的管理服务系统应用程序。管理服务系统应用程序通过调用Win32 子系统API,向内核下的管理服务系统驱动程序传递IRP,内核收到IRP 后,跟据收到的IRP 的内部信息,执行相应的派遣函数,对相应内存进行读写,从而给管理服务系统应用程序提供可用的系统信息。
管理系统总体结构图
图3 管理系统总体结构图。
31 内核模式下读取系统信息
线程管理服务系统驱动程序中,读取系统信息的方法用到了微软没有公开文档的内核服务函数,ZwQuerySystemInformATIon,这个函数被封装在ntdlldll模块中,通过链接ntdlllib 可得到此函数地址。通过一个枚举量SystemProcessInformation 来得到进程线程相关信息,填入到第二个输入参数SYSTEM_PROCESS_INFORMATION结构中, 这样就获得了当前系统关于进程线程的信息。
32 内核模式下枚举系统进程线程
SYSTEM_PROCESS_INFORMATION结构中存储了进程及其线程的所有相关信息,表1 列出了它的具体内容,包括结构内域的地址偏移, 数据类型和描述。
SYSTEM_PROCESS_INFORMATION的第一个DWORD型是下一个进程SYSTEM_PROCESS_INFORMATION相对于当前结构地址的偏移量,可以通过地址偏移来遍历所有的进程结构,当遇到某一个进程结构的0 x 0 0 0 0 处的DWORD 型值为0 时,说明这个结构体是系统内最后一个结构体。线程管理服务在它的派遣函数中通过这种方式遍历所有进程,从中提取有用的信息,填入两个自定义结构体中。如图4 所示,描述了一个具有两个线程的进程的数据结构,首先在MY_PROCESS_INFO 结构中填入进程的相关信息,然后根据此进程所有的线程数,向系统申请足够大的分页内存空间,PVOID 型指针指向的是第一个线程结构所在的地址空间,然后向线程结构体中_MY_THREAD_INFO 中填入线程信息,再由线程结构体中的PVOID 型指针指向第二个线程结构体所在的地址空间,以此类推,最后一个线程结构体的PVOID型指针指向NULL。这样一个过程描述了一个进程及其所属的所有线程的枚举过程,通过对所有进程的遍历,可以得到系统中的一个完整的进程线程表,存在一段分页内存中,这样在应用程序中便可以得到这些信息。
表1 SYSTEM_PROCESS_INFORMATION 结构
SYSTEM_PROCESS_INFORMATION 结构
进程线程的两种数据结构
图4 进程线程的两种数据结构。
33 线程管理服务系统应用程序设计
进程管理服务系统应用程序是要通过调用Win 32子系统的API 函数DeviceIoControl 来向线程管理服务系统驱动程序发送IRP 的,然后在IRP 结束之后把驱动程序中读出的所有有用进程线程信息填入到指定的内存中。这样线程管理服务系统应用程序就可以根据所获得的系统信息句柄来对线程CPU 亲缘性属性进行设置。首先为DeviceIoControl 中的InputBuffer 申请一段内存空间传入给驱动程序,驱动程序读取内核空间进程线程信息写入到这段内存中,应用程序读到信息并显示给用户。
在系统中应用程序为每一个CPU 维护一个结构体,内容包括该CPU 是否运行实时线程,该CPU 上运行的线程数(如果是实时线程CPU线程数为1),以及在此CPU上运行的线程结构数组的首地址。系统通过对此CPU 结构数组的解析来对线程进行管理。并通过DeviceIoControl函数把设置后的CPU 结构交给驱动程序内核。
34 修改Windows 内核结构体
在驱动程序读回应用程序下用户的设置结果后,就需要按照用户的设定修改KTHREAD 下的Affinity 域的掩码值了。首先要找到KTHREAD 的线性内存空间,PsGetCurrentProcess()内核函数可以返回内核下当前进程空间的E P R O C E S S 结构。E P R O C E S S 结构下的ActiveProcessLinks 域是LIST_ENTRY 结构,通过它可以遍历所有的ETHREAD 结构,那么那到KTHREAD 下的Affinity 域就不难了,可以使用两个循环嵌套来得到所有线程的Affinity 域并将其值设为应用程序中用户的设定值。线程CPU 掩码就被成功的修改了。当CPU 被设定为运行实时线程的CPU 时,在它上面运行的线程只能是一个实时线程,这时的运行线程数被设定为1; 当CPU被设定为非实时线程的时候,上面有可能除了任务线程运行之外,还有Windows 系统进程下的线程。
4 软件使用及性能测试
41 驱动的加载及软件的使用
首先需要把本系统的驱动sys 文件加载到Windows的服务管理器中,加载成功后打开应用程序,用户可以通过应用程序中显示出的当前系统内的进程和线程进行选择,并在GUI 图形界面中对其CPU 占用率及CPU亲缘性进行设置。
42 设置 CPU 亲缘性测试
测试运行在双核CPU 的PC 机上,系统运行一个要测试的任务线程(任务线程为一个108 次加法运算),四个其它线程(为测试方便,设为while 循环线程),限定了循环线程的CPU 亲缘性掩码为0x0001,任务线程的CPU亲缘性为0x0002,这样任务线程与其它线程分别在两个核上运行,分别测试了任务线程单独运行,任务线程与其它线程不设定CPU 亲缘性,任务线程与其它线程设定CPU 亲缘性三种情况下下任务线程的运行总时间如表2 所示。
表2
从表2 分析, 设定任务线程的CPU 亲缘性与其它线程所占用的CPU 分开,真正意义上的实现了任务的异步执行,非常有效的提高了实时线程对CPU 资源的使用率。
5 结束语
本文分析了Windows 系统的内核进程线程调度表2CPU 亲缘性设定三种情况下任务线程运行时间表机制,并在此基础上设计了一种基于Windows *** 作系统内核驱动的多核CPU 线程管理方法, 实现了这样一个软件系统。首先在Windows 内核层获取系统进程线程信息,然后再把信息传入应用层,由应用层上的应用程序根据获取的信息句柄,对进程进行 *** 作,用户在图形界面下按照仿真任务对CPU 资源的不同需求,进行相应的设置,可以为指定线程设置CPU 亲缘性的功能。在一定程度上为Windows 系统下的任务合理地分配了CPU 资源,为对实时性要求较高的任务提供了一个可靠的运行环境。
#include <tlhelp32h>
int GetProcessThreadList(DWORD th32ProcessID) //进程的ID
{
HANDLE hThreadSnap;
THREADENTRY32 th32;
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);
if (hThreadSnap == INVALID_HANDLE_VALUE)
{
return 1;
}
th32dwSize = sizeof(THREADENTRY32);
if (!Thread32First(hThreadSnap, &th32))
{
CloseHandle(hThreadSnap);
return 1;
}
do
{
if (th32th32OwnerProcessID == th32ProcessID)
{
printf("ThreadID: %ld\n", th32th32ThreadID); //显示找到的线程的ID
}
}while(Thread32Next(hThreadSnap, &th32));
CloseHandle(hThreadSnap);
return 0;
}
故障解决:
1当您收到此错误时重新启动机器,因为在某些情况下重新启动Windows可能会有所帮助。
2故障排除的第一步,您必须在管理命令提示符中运行SFC / SCANNOW命令。
3如果系统文件检查器发现损坏的文件修复。
4在设备管理器中收到此错误的用户应首先更新其系统上的所有现有设备驱动程序。
5如果在Windows上使用的某些第三方软件可能会中断本机管理单元,从而导致此类错误。所以如果你使用很多第三方工具,你可以尝试干净启动。只需运行msconfig命令,请转到服务选项卡,然后选中隐藏所有Microsoft服务,然后单击禁用所有按钮。重新启动并检查问题的状态。也可以逐步执行,对于每个程序,逐一禁用相应的服务,并在问题解决时继续检查。
6如果问题仍然存在,重新装电脑系统解决。
以上就是关于在Windows下多进程的实现全部的内容,包括:在Windows下多进程的实现、windows线程与窗口之间的关系、Windows环境下如何使用进程,何时使用线程等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)