1.线程控制
终止线程
方式一:ExitThread(结束码)
该函数在被调用过后,会告诉操作系统来关闭线程
结束码:ExitThread调用结束后,会返回一个结束码,我们可以通过这个结束码来判断线程是正常结束还是异常
一旦该函数被调用完毕,就会销毁堆栈
存在弊端:如果我们在堆栈中有mallc等分配空间的代码,一旦ExitThread函数执行后,就会销毁堆栈,因此我们分配的控件就得不到free的机会,那么就会存在内存泄露的风险
并且该函数,如果是放在回调函数中,那么系统结束的就是主线程,因此在我们的实验中,如果ExitThread函数是放在线程函数中,那么就是终止的线程,对话框DialogBox并没有消失
但是如果是放在回调函数中,那么主程序就会被终止,但是前面我们创建的线程却没有终止,所以就出现了程序未结束的情况
方式二:线程函数返回
意思就是让线程函数正常销毁,在终止之前将要做的事,比如释放空间等代码写在终止进程之前
while (dwTimer<10000)
{
if (dwTimer == 10)
{
break;
}
Sleep(0);
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "%d", ++dwTimer);
SetWindowText(hEdit, buffer);
}
//这里就可以写自己用来释放资源的代码
return 0;
方式三:::TerminateThread(hThread,2)
同步调用:ExitThread函数是在当前线程对线程进行终止的,一旦线程被终止,得到返回值,线程就结束了,ExitThread函数下面的代码就不会继续了
异步调用:这里TerminateThread函数是在另一个线程对当前线程进行终止,TerminateThread告诉操作系统终止当前线程后,就继续往下走了,即系统将线程终止前,代码可能会来到TerminateThread函数下面的代码,存在隐患,因此我们需要用到::WaitForSingleObject(hThread,INFINITE)函数
注意:该函数终止线程的时候不会销毁堆栈,因此存在问题
2.线程ConText结构体
思考:
每个线程在执行的时候,都会独自占用一个CPU,当系统中的线程数量 > CPU的数量时,就会存在多个线程共用一个CPU
的情况。但CPU每次只能运行一个线程,Windows每隔20毫秒会进行线程的切换,那比如线程A执行到地址:0x2345678
eax:1 ecx:2 edx:3 ebx:4...还有eflag标志寄存器中的值等等。。。
此时,线程执行时间到了,被切换到了线程B。。。。当线程B的时间片也到了,再切换会线程A时,系统是如何知道该
从哪个地址开始执行呢?被切换前用到的各种寄存器的值该如何恢复呢?
这时就要用到ConText结构体了,每次切换线程之前,系统就把当前各种寄存器的数据存入结构体中,下次又轮到该线程时,又可以根据前面的数据继续执行
CONTEXT:
该结构包含了特定处理器的寄存器数据。
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;
首先我们要将线程挂起,否则线程仍在继续,那当前结构体中的数据就没有意义了
这里context.ContextFlags成员会根据我们不同的赋值,我们可以得到不同的成员
当赋值CONTEXT_FULL时,就包含debug寄存器以外的所有成员
赋值CONTEXT_DEBUG_REGISTERS加CONTEXT_FULL就可以包含所有成员
这里我们使用SetThreadContext函数可以修改系统中ConText结构体的值
3.作业
// CreateThread1.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
#include<Windows.h>
HWND hEdit ;
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwIndex = 0;
DWORD dwCount;
while(dwIndex<10000)
{
GetWindowText(hEdit,szBuffer,10);
sscanf( szBuffer, "%d", &dwCount );
dwCount++;
memset(szBuffer,0,10);
sprintf(szBuffer,"%d",dwCount);
SetWindowText(hEdit,szBuffer);
dwIndex++;
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwIndex = 0;
DWORD dwCount;
while(dwIndex<10000)
{
GetWindowText(hEdit,szBuffer,10);
sscanf( szBuffer, "%d", &dwCount );
dwCount++;
memset(szBuffer,0,10);
sprintf(szBuffer,"%d",dwCount);
SetWindowText(hEdit,szBuffer);
dwIndex++;
}
return 0;
}
BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
BOOL bRet = FALSE;
switch(uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
hEdit = GetDlgItem(hDlg,IDC_EDIT1);
SetWindowText(hEdit,"0");
break;
}
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_BUTTON1:
{
HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,
NULL, 0, NULL);
::CloseHandle(hThread1);
return TRUE;
}
case IDC_BUTTON2:
{
HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2,
NULL, 0, NULL);
::CloseHandle(hThread2);
return TRUE;
}
}
break ;
}
return bRet;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);
return 0;
}
一个cpu只能运行一个线程,当多个线程使用一个cpu时,就需要通过快速的切换进程来实现进程之间的多个运行,因此在该代码中,如果每个线程的运行时间较短,能够快速结束时,那么对于进程就没有太大影响
但是如果每个线程运行时长都比较长时,在这期间,如果用户点击按钮触发了另一个线程,就会出现线程之间抢占的现象,但是抢占之后系统并不会自动切换到另一个未完成的线程那里,就导致了错误的发生