线程控制

1.线程控制
控制线程状态的API函数:
//挂起线程:                
::SuspendThread(hThread);                
                
//恢复线程:                
::ResumeThread(hThread);                
                
//终止线程:                
    //方式一:                
    ::ExitThread(DWORD dwExitCode);  
        //参数中没有线程句柄,在线程函数中使用;
        //参数dwExitCode为线程退出码,将作为线程处理函数的返回值,用来描述线程的非正常退出
        //每一个线程都有自己的堆栈,该函数终止的线程会清理堆栈;
        //该函数一旦调用,线程将不能再被操作,但该函数并不会释放堆中的内存,因此存在内存溢出的隐患;  
       
    //方式二:线程函数返回   
       //线程函数执行完后会结束线程,因为是正常结束,可以自己写代码来释放堆中申请的内存;              
                
    //方式三:                
    ::TerminateThread(hThread,2);    //第二个参数为线程退出码                
    ::WaitForSingleObject(hThread,INFINITE); 
        //TerminateThread并不会清理堆栈,这样的好处是其它地方用堆栈中的变量时不会出问题;     
           
//判断线程是否结束                
BOOL GetExitCodeThread(                
  HANDLE hThread,                
  LPDWORD lpExitCode                
);                
    //STILL_ACTIVE //正在运行                
    //参数:                                
        //hThread: 要结束的线程句柄                                
        //dwExitCode: 指定线程的退出代码。可以通过GetExitCodeThread来查看一个线程的退出代码         
ExitThread和TerminateThread的区别:
    都是用来结束线程的,ExitThread是同步的,也就是ExitThread执行完后程序才能继续往下执行;
    TerminateThread是异步调用;也就是单独起了一个线程来让目标线程结束; 
    TerminateThread调用后不能保证立即结束线程,在后面写线程结束后要执行的代码可能有问题;
    解决办法是用 WaitForSingleObject,该函数会阻塞线程直到TerminateThread的目标线程真正结束;
 
线程挂起后恢复时并不会立即运行;
因为windows并不是实时的操作系统,只有在调度程序给可调度状态的线程分配cpu时间时才会运行;
因此windows中无法实现在某个确定的时间点运行一个线程;
 
2.线程contex结构
每个线程在执行的时候,都会独自占用一个CPU,
当系统中的线程数量 > CPU的数量时,就会存在多个线程共用一个CPU的情况。
但CPU每次只能运行一个线程,Windows每隔20毫秒会进行线程的切换,
那比如线程A执行到地址:0x2345678 eax:1 ecx:2 edx:3 ebx:4...还有eflag标志寄存器中的值等等。。。             
此时,线程执行时间到了,被切换到了线程B。。。。
当线程B的时间片也到了,再切换回线程A时,系统是如何知道该从哪个地址开始执行呢?被切换前用到的各种寄存器的值该如何恢复呢?
 
context结构:该结构包含了特定cpu的寄存器数据
typedef struct _CONTEXT {                            
                            
    //                            
    // The flags values within this flag control the contents of                            
    // a CONTEXT record.                            
    //                            
    // If the context record is used as an input parameter, then                            
    // for each portion of the context record controlled by a flag                            
    // whose value is set, it is assumed that that portion of the                            
    // context record contains valid context. If the context record                            
    // is being used to modify a threads context, then only that                            
    // portion of the threads context will be modified.                            
    //                            
    // If the context record is used as an IN OUT parameter to capture                            
    // the context of a thread, then only those portions of the thread's                            
    // context corresponding to set flags will be returned.                            
    //                            
    // The context record is never used as an OUT only parameter.                            
    //                            
                            
    DWORD ContextFlags;                            
                            
    //                            
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is                            
    // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT                            
    // included in CONTEXT_FULL.                            
    //                            
                            
    DWORD   Dr0;                            
    DWORD   Dr1;                            
    DWORD   Dr2;                            
    DWORD   Dr3;                            
    DWORD   Dr6;                            
    DWORD   Dr7;                            
                            
    //                            
    // This section is specified/returned if the                            
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.                            
    //                            
                            
    FLOATING_SAVE_AREA FloatSave;                            
                            
    //                            
    // This section is specified/returned if the                            
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.                            
    //                            
                            
    DWORD   SegGs;                            
    DWORD   SegFs;                            
    DWORD   SegEs;                            
    DWORD   SegDs;                            
                            
    //                            
    // This section is specified/returned if the                            
    // ContextFlags word contians the flag CONTEXT_INTEGER.                            
    //                            
                            
    DWORD   Edi;                            
    DWORD   Esi;                            
    DWORD   Ebx;                            
    DWORD   Edx;                            
    DWORD   Ecx;                            
    DWORD   Eax;                            
                            
    //                            
    // This section is specified/returned if the                            
    // ContextFlags word contians the flag CONTEXT_CONTROL.                            
    //                            
                            
    DWORD   Ebp;                            
    DWORD   Eip;                            
    DWORD   SegCs;              // MUST BE SANITIZED                            
    DWORD   EFlags;             // MUST BE SANITIZED                            
    DWORD   Esp;                            
    DWORD   SegSs;                            
                            
    //                            
    // This section is specified/returned if the ContextFlags word                            
    // contains the flag CONTEXT_EXTENDED_REGISTERS.                            
    // The format and contexts are processor specific                            
    //                            
                            
    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];                            
                            
} CONTEXT;                            
                            
 
进程就是程序的4GB空间,线程就是EIP;
当一个线程切换时,为了再次执行时能接着执行,会将寄存器的信息保存context结构中;
 
如何获取context结构中的值:
例如:获取context中的eip
//挂起线程;不挂起获取的值不准确                    
SuspendThread(线程句柄);                    
                    
CONTEXT context;                        
                    
//设置要获取的类型                    
context.ContextFlags = CONTEXT_CONTROL;                    
                    
//获取                    
BOOL ok = ::GetThreadContext(hThread,&context);                    
                    
//设置                    
context.Eip = 0x401000;     
               
SetThreadContext(hThread,&context);         

 3.线程控制实例代码

#include "windows.h"
#include "stdio.h"
#include "resource.h"

HWND plus;
HANDLE hThread;

//线程函数
DWORD WINAPI doPlus(LPVOID param){
    //获取文本框内容
    TCHAR szBuffer[10];
    memset(szBuffer, 0, 10);
    GetWindowText(plus, szBuffer, 10);
    
    //字符串转数字
    DWORD num;
    sscanf(szBuffer, "%d", &num);

    //计算后写回文本框
    while(num < 1000){
        memset(szBuffer, 0, 10);
        Sleep(1000);
        sprintf(szBuffer, "%d", ++num);
        SetWindowText(plus, szBuffer);
    }
    return 0;
}

//回调函数
BOOL CALLBACK DialogProc(                                    
                         HWND hwndDlg,  // handle to dialog box            
                         UINT uMsg,     // message            
                         WPARAM wParam, // first message parameter            
                         LPARAM lParam  // second message parameter            
                         )            
{    
    switch(uMsg)                                
    {    
    case WM_INITDIALOG :
        plus = GetDlgItem(hwndDlg, IDC_EDIT1);
        SetWindowText(plus, TEXT("0"));
        return TRUE;      
                                    
    case  WM_COMMAND :                                
        switch (LOWORD (wParam))                            
        {
        case IDC_BTN_START:  
            hThread = ::CreateThread(NULL, 0, doPlus, NULL, 0, NULL);
            return TRUE;  
        case IDC_BTN_WAIT:
            ::SuspendThread(hThread);                
            return TRUE;
        case IDC_BTN_RETURN:
            ::ResumeThread(hThread);                
            return TRUE;
        }
        break ;   
        
    case WM_CLOSE:
        EndDialog(hwndDlg, 0);
        return TRUE;
    }    
                                    
    return FALSE ;                                
}

int CALLBACK WinMain(
                     HINSTANCE hInstance,
                     HINSTANCE hPrevhInstance,
                     LPSTR lpCmdline,
                     int nCmdShow
                     ){
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc);

    return 0;
}

结果:

线程控制