进程和线程
1.进程与线程
程序:
当代码写完后,编译会生成一个.exe文件,存储在硬盘中;这就是程序;
内存镜像:
exe文件双击执行时,会加载到内存中;
exe文件在内存中的状态和在文件中的状态不同;
内存中的状态是pe结构拉伸后的状态;
pe结构拉升后的状态称为内存镜像;
pe文件的内存镜像并不一定可以运行,只是具备了运行的条件;
如果内存镜像文件要能够运行,需要创建一个线程,并将eip交给线程;此时内存镜像就可以运行也就变成了进程;
总之在硬盘中保存的exe文件就是程序;
跑起来的程序就是进程;
一个进程中至少会有一个线程;
打开任务管理器:
可以看到进程idea64.exe,有47个线程,pid是10432等等;
2.多线程程序
每一个进程都有一个独立的4gb虚拟空间;
进程就是用来描述这4gb空间中放什么;
线程就是进程中有几个同时执行的程序;
创建新线程的API函数:只管创建线程不管线程的功能
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性 通常为NULL SIZE_T dwStackSize, // 参数用于设定线程可以将多少地址空间用于它自己的堆栈 // 每个线程拥有它自己的堆栈;设0系统会自动给线程分配空间 LPTHREAD_START_ROUTINE lpStartAddress, // 参数用于指明想要新线程执行的线程函数的地址;函数地址是一个全局变量,函数名就是该全局变量 LPVOID lpParameter, // 线程函数的参数 // 在线程启动执行时将该参数传递给线程函数 // 既可以是数字,也可以是指向包含其他信息的一个数据结构的指针 DWORD dwCreationFlags, // 0 创建完毕立即调度 CREATE_SUSPENDED创建后挂起 LPDWORD lpThreadId // 线程ID; out参数,线程是内核创建的创建完成后会生成一个线程id,用来唯一标识该线程 ); // 返回值:线程句柄,3环程序想要操作该线程需要获取线程句柄
线程句柄与线程ID:
线程是由Windows内核负责创建与管理的,句柄相当于一个令牌,有了这个令牌就可以使用线程对象.
线程ID是身份证,唯一的,系统进行线程调度的时候要使用的.
实例:
#include<stdio.h> #include<windows.h> //线程函数 DWORD WINAPI ThreadProc( LPVOID lpParameter // 给线程传递参数 ){ for(int i=0;i<100;i++){ Sleep(1000); printf("-----------%d\n", (int)lpParameter); } return 0; } //创建线程 void fun(){ int x = 2; //用来传到线程中的参数 //创建一个新的线程 HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, (void*)x, 0, NULL); //不能直接传递x的地址,因为线程切换时堆栈会销毁,存在堆栈中的临时变量的地址将不准确 //如果不在其他的地方引用它 关闭句柄 ::CloseHandle(hThread); } //主函数 void main(){ fun(); for(int i=0; i<100;i++){ Sleep(1000); printf(">>>>>>>>>>>>\n"); } }
结果:
可以看到有两种输出;
说明主线程和创建的新线程在交替运行;
也就是说进程的4gb空间可以有1个线程干活,也可以有多个线程干活;
向线程函数传递变量的两种方式:
全局变量
线程参数
3.利用多线程实现的倒计时程序
目标:
点击开始后,第一个文本框的值从1000每隔1秒种减1 直到0
第二个文本框从0每隔1秒钟加1 直到1000
用到的API函数:
//文本框赋值: SetWindowText(句柄,数据缓存区); //文本框取值: GetWindowText(句柄,数据缓冲区,长度); //数字转字符: sprintf(数据缓冲区,"%d",数字); //字符转数字: sscanf( szBuffer, "%d", &dwTimer ); //获取子窗口: GetDlgItem(hDlg,IDC_EDIT_TIMER);
代码:
#include<windows.h> #include<stdio.h> #include "resource.h" HWND sub; HWND plus; //倒计时;需要单独的线程来执行该功能,因为主线程需要用来处理消息循环不能被占用; DWORD WINAPI doSub(LPVOID lpParameter){ //MessageBox(NULL,TEXT("go"),TEXT("GO"),MB_OK); //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(sub, szBuffer, 10); //字符转数字 DWORD time; sscanf(szBuffer, "%d", &time); //计算并写回文本 while(time > 0){ memset(szBuffer, 0, 10); Sleep(1000); sprintf(szBuffer,"%d", --time); SetWindowText(sub,szBuffer); } return 0; } //顺计时 DWORD WINAPI doPlus(LPVOID lpParameter){ //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(plus, szBuffer, 10); //字符转数字 DWORD time; sscanf(szBuffer, "%d", &time); //计算并写回文本 while(time < 1000){ memset(szBuffer, 0, 10); Sleep(1000); sprintf(szBuffer,"%d", ++time); 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 : { //初始化文本框 sub = GetDlgItem(hwndDlg,IDC_SUB); SetWindowText(sub,TEXT("1000")); plus = GetDlgItem(hwndDlg, IDC_PLUS); SetWindowText(plus,TEXT("0")); } return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BTN_GO: HANDLE hThread = ::CreateThread(NULL, 0, doSub, NULL, 0, NULL); //如果不在其他的地方引用它 关闭句柄 ::CloseHandle(hThread); HANDLE hThread2 = ::CreateThread(NULL, 0, doPlus, NULL, 0, NULL); ::CloseHandle(hThread2); 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; }
结果: