一 进程
1.1进程定义
进程是一个容器,包含程序执行需要的代码、数据、资源等等信息。Windows是多任务操作系统,可以同时执行多个进程。
1.2.Windows进程的特点
1)每个进程都有自己的ID号
2)每个进程都有自己的地址空间,进程之间无法访问对方的地址空间。
3)每个进程都有自己的安全属性
4)每个进程当中至少包含一个线程
1.3.进程的id和句柄
获取当前进程ID: DWORD GetCurrentProcessId()
得到当前进程的句柄: HeANDLE GetCurrentProcss( VOID );
为了安全,返回一个假的句柄,但可以直接使用假的句柄操作当前线程
1.4创建进程
BOOL CreateProcess(
LPCWSTR lpApplicationName,//进程路径
LPWSTR lpCommandLine,//命令行参数
LPSECURITY_ATTRIBUTES lpP,//进程安全属性
LPSECURITY_ATTRIBUTES Attribu,//线程安全属性
BOOL bInheritHandles,//进程的句柄继承
DWORD dwCreationFlags,//创建方式
LPVOID lpEnvironment,//环境信息
LPCWSTR lpCurrentDirectory,//当前目录
LPSTARTUPINFOW lpStartupInfo,//起始信息
LPPROCESS_INFORMATION lp//进程句柄(传出)
);
1.5等待进程结束
WaitForSingleObject( 进程句柄, INFINITE );
INFINITE : 无限制的等待
1.6打开进程—得到进程句柄
HANDLE OpenProcess(
DWORD dwDesiredAccess,//打开权限PROCESS_ALL_ACCESS
BOOL bInheritHandle,//继承方式
DWORD dwProcessId//进程id );
1.7结束当前进程
Void ExitProcess(UINT uExitCode退出码);
1.8结束指定进程
TerminateProcess(进程句柄,退出码)
二. 线程
2.1 线程定义
windows以线程为单位进行调度程序,一个程序中至少有一个线程
可以有多个线程,用来实现多任务的处理
2.2线程特点
线程都有一个id
线程都具有自己的安全属性
线程都有自己的内存栈
线程都有自己的寄存器信息
2.3创建线程:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpT,//安全属性
SIZE_T dwStackSize,//线程栈大小
LPTHREAD_START_ROUTINE lp,//线程处理函数
LPVOID lpParameter,//处理函数参数
DWORD dwCreationFlags,//创建方式
LPDWORD lpThreadId//线程id );//线程句柄
创建标志:0---创建后立刻执行
CREATE_SUSPENDED-创建之后线程处于挂起
2.4线程处理函数
DWORD WINAPI Threadproc(LPVOID lp){ 参数是需要传入的}
2.5结束线程
BOOL TerminateThread(
HANDLE hThread,//线程句柄
DWORD dwExitCode//退出码);
结束当前线程
VOID ExitThread(DWORD dwExitCode退出码)
关闭线程句柄:closeHandle
2.6等候进程结束
DWORD WaitForSingleObject(
HANDLE hHandle, //句柄
DWORD dwMilliseconds//等时间);
DWORD WaitForMultipleObjects(
DWORD nCount,//句柄数量
HANDLE *lpHandles,//句柄地址
BOOL bWaitAll,//等方式
DWORD dwMilliseconds//等时间 );
等待方式:TRUE---所有句柄有信号才结束
FALSE---一个句柄有信号就返回
//示例代码 (在test中开启多个进程, 然后逐一关闭)
// test10.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #include "test10.h" //进程 通常被定义为一个正在运行的程序的实例 //由两个部分组成: //1、操作系统用来管理进程的内核对象 //2、地址空间,包含可执行模块,dll模块 //进程和线程 地主和长工 //进程的活动性不强,如果进程要去完成某项操作,它必须拥有一个在它的环境中运行的线程,进程拥有主线程,线程负责 //执行包含在进程的地址空间的代码 //量程 操作系统为每一个线程安排一定的cpu时间,操作系统通过一种循环的方式为线程提供时间片(称为量程) //进程的特点:1、进程的活动性不强;2、每一个进程都有一个与之匹配的主线程;3、线程才是去执行地址空间的代码 #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TEST10, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST10)); // 主消息循环: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST10)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TEST10); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // HINSTANCE g_hInst[10];//实例句柄,进程是一个实例 int num = 0;//用来记录进程个数 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_LBUTTONDOWN: { if (num < 10) { STARTUPINFOW si;//指定新进程的主窗口特性 ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi;//进程的相关资源信息 switch (num) { case 0: CreateProcess(_T("C:/Program Files (x86)/极速PDF阅读器/JisuPdf.exe"),//创建新进程的路径文件名 NULL,//进程命令行参数 NULL,//进程是否可以被子进程继承 NULL,//线程是否可以被子进程继承 FALSE,//新进程是否从调用进程处继承了句柄 0,//进程优先级的标记 NULL,//指向一个新进程的新环境块 NULL,//用来指定子进程的工作路径 &si,//用来指定新进程的主窗体如果显示的结构体 &pi//指向新进程的识别信息 ); break; default: //创建进程 CreateProcess(_T("C:/Windows/notepad.exe"),//创建新进程的路径文件名 NULL,//进程命令行参数 NULL,//进程是否可以被子进程继承 NULL,//线程是否可以被子进程继承 FALSE,//新进程是否从调用进程处继承了句柄 0,//进程优先级的标记 NULL,//指向一个新进程的新环境块 NULL,//用来指定子进程的工作路径 &si,//用来指定新进程的主窗体如果显示的结构体 &pi//指向新进程的识别信息 ); } g_hInst[num++] = (HINSTANCE)pi.hProcess; } } break; case WM_RBUTTONDOWN: //进程的退出的4种情况: //1、入口函数的返回(推荐) //2、当前进程调用 TerminateProcess 结束参数中表示的进程 //3、当前进程调用 ExitProcess 结束当前进程 //4、进程自动终止(很少见) if (num > 0) { TerminateProcess(g_hInst[--num], 0); } else ExitProcess(0); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // “关于”框的消息处理程序。 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }
//多线程处理的示例
// test10-thread.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "windows.h" //线程:长工 //分成两个部分:内核对象(操作系统用来对线程来进行管理) // 线程堆栈(用来维护线程当中运行的时候需要的函数的参数和临时变量) //线程可以理解为一个代码的执行序列,在某个进程的环境中创建,所以寿命也在该进程当中 //线程所需要的开销要比进程要少,在编程的过程,尽量通过增加线程来解决程序问题(多线程) //量程-cpu时间片-分配给线程 //多线程:就是用来同时处理多个任务,异步处理多个代码序列 int num = 10;//票的数量及票的id void aa(LPVOID v) { while (true) { if (num > 0) { printf("第%d个代售点卖出第%d张票\n", (int)v, num); //num--; num = num - 1; } else break; } } int _tmain(int argc, _TCHAR* argv[]) { int id = 1; HANDLE hThread1,hThread2,hThread3,hThread4; hThread1 = CreateThread(NULL, 0,//线程堆栈大小,默认0 (LPTHREAD_START_ROUTINE)aa,//线程处理的函数 (LPVOID)id,//传递给线程函数的辅助信息 0,//线程创建时的参数,默认给0 NULL//线程ID ); id++; hThread2 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)aa,(LPVOID)id,0,NULL); id++; hThread3 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)id, 0, NULL); id++; hThread4 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)id, 0, NULL); while (true) { if (num > 0) { printf("火车站卖出第%d张票\n", num); num--; } else break; } //线程的退出的4种情况: //1、函数的返回(推荐) //2、当前线程调用 TerminateThread 结束参数中表示的线程 //3、当前线程调用 ExitThread 结束当前线程 //4、线程自动终止(很少见) TerminateThread(hThread1,0); TerminateThread(hThread2, 0); TerminateThread(hThread3, 0); TerminateThread(hThread4, 0); return 0; }
三.线程同步
3.1原子锁
对单条指令进行操作,当多个线程同时对同一个资源进行操作的时,由于线程间资源的抢占,会导致操作的结果丢失或者不是我们预期的。例如a++
interLockedIncrement、加
interlockedDecrement减
InterlockedExchange交换
3.2临界区
CRITICAL_SECTION cs = { 0 };
InitializeCriticalSection() //
EnterCriticalSection( &cs );
LeaveCriticalSection( &cs );
DeleteCriticalSection( &cs );
3.3事件
3创建事件:
HANDLE CreateEvent(
LPSECURIT utes,// 安全属性
BOOL bManualReset,//重置方式false自动 true手动
BOOL bInitialState,//初始状态ture有信号
LPCSTR lpName//事件名
);成功返回事件句柄
等候事件 waitforsingleobject/xxxMultiple
触发事件: setEvent(事件句柄)
resetEvent()
关闭事件CloseHandle
3.4 互斥
HANDLE CreateMutex(
LPSECURITY_A butes,//安全属性
BOOL bInitialOwner,//初始拥有者
LPCSTR lpName//互斥命名);
拥有者:调用createMtex的拥有互斥
False 创建的时候没有线程拥有互斥
等候互斥 WaitForSingleObject
释放互斥ReleaseMutex(互斥量)
关闭句柄 closehandle
3.5 信号量
3.5.1创建信号量
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSees,//安全属性
LONG lInitialCount,//初始化信号量数量
LONG lMaximumCount,//信号量最大值
LPCSTR lpName//命名);
等候信号量:等候通过一次 信号量的信号减一,0堵塞
3.5.2释放信号量
BOOL ReleaseSemaphore(
HANDLE hSemaphore,//信号量句柄
LONG lReleaseCount,//释放数量
LPLONG lpPreviousCount//释放前数量 );
关闭句柄+
示例代码 :如下
// test11.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> //线程通讯 //线程需要在两种情况下需要通讯 //1、如果有多个线程访问共享资源而不能够让这个共享资源被破坏 //2、当一个线程将某个任务完成,通知另一个线程接着完成这一系列的任务时 //线程互斥 //一、用户区间 二、内核对象(并不意味只能做线程互斥) //一、用户区间 //临界区 临界区 1、定义一个临界区的变量 //CRITICAL_SECTION g_cs; //二、内核对象 事件 基于内核对象的同步机制 进程和线程也属于内核对象,对于内核来说每种对象都可以处于已通知和未通知两种状态,状态的切换是由系统规则来决定 当进程在运行的过程中,进程内核对象处于未通知状态,当进程结束时,变为已通知 事件 1、事件句柄定义 //HANDLE g_hEvent; 互斥内核对象 能够确保线程对于单个资源的互斥访问权 互斥内核对象包含一个使用数量,一个线程id和一个递归的计数器 互斥对象的行为特性和临界区相同,互斥属于内核,临界区属于用户 id用于标识系统中哪个线程当前拥有互斥对象,递归计数器用于指明该线程拥有互斥对象的次数 互斥对象的使用规则:如果线程id为0,这代表无效id,互斥对象没有被线程拥有,互斥对象发出通知信号,id非0,证明有 线程在拥有互斥,不发出信号 // 互斥对象 1、互斥对象的句柄定义 //HANDLE g_hEutex; //信号量 信号量用于对资源进行计数 //信号量的使用规则: //1、当前的资源数量大于0,可以去使用资源,发出信号 //2、当前的资源数量是0,没有可使用资源,不发出信号 //3、系统不允许资源数量为负数 //4、当前可以使用的资源数量不能大于最大的资源数量 //信号量 1、句柄的定义 HANDLE g_hSemaphore; int num = 50; void aa(LPVOID v) { while (true) { 临界区 3、进入临界区 //EnterCriticalSection(&g_cs); 事件 3、等待通知 //WaitForSingleObject(g_hEvent, INFINITE);//使用线程自愿等待,等待到已通知 互斥 3、等待通知 //WaitForSingleObject(g_hEutex, INFINITE);//使用线程自愿等待,等待到已通知 //信号量 3、等待通知 WaitForSingleObject(g_hSemaphore, INFINITE);//使用线程自愿等待,等待到已通知 if (num > 0) { printf("第%d个代售点卖出%d张票\n",(int)v,num); num--; } else { 临界区 4、离开临界区 //LeaveCriticalSection(&g_cs); 事件 4、设置事件通知 //SetEvent(g_hEvent); 互斥 4、重置互斥内核 //ReleaseMutex(g_hEutex); //信号量 4、重置信号量 //第2个参数为重置当前的资源数量 ReleaseSemaphore(g_hSemaphore, 1, NULL); break; } 临界区 4、离开临界区 //LeaveCriticalSection(&g_cs); 事件 4、设置事件通知 //SetEvent(g_hEvent); 互斥 4、重置互斥内核 //ReleaseMutex(g_hEutex); //信号量 4、重置信号量 //第2个参数为重置当前的资源数量 ReleaseSemaphore(g_hSemaphore, 1, NULL); } } int _tmain(int argc, _TCHAR* argv[]) { 临界区 2、临界区变量的初始化 //InitializeCriticalSection(&g_cs); 事件 2、事件的初始化 第2个参数表示是自动事件(自动重置)还是手动事件(人工重置),人工重置的事件得到通知的时候,等待这个事件的所 有线程都已通知,false代表人工重置 第3个参数代表着已通知 //g_hEvent = CreateEvent(NULL, false, true, NULL); 互斥 2、互斥的初始化 第2个参数为false,代表互斥对象拥有的id和计数器都为0,给true,代表互斥对象的id为调用线程的id,计数器加1 (id为0,代表互斥对象没有线程占用,发出通知信号,如果id非0,证明有线程拥有这个互斥对象,不发出通知信号) //g_hEutex = CreateMutex(NULL, false, NULL); //信号量 2、信号量的初始化 //第2个参数是初始化资源数量,如果大于0,发出通知信号,如果资源为0,不发出信号,不允许为负数,不允许超过第3个参数 //第3个参数是最大的资源数量 g_hSemaphore = CreateSemaphore(NULL, 1, 1, NULL); int id = 1; HANDLE hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)id, 0, NULL); id++; HANDLE hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)id, 0, NULL); id++; HANDLE hThread3 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)id, 0, NULL); id++; HANDLE hThread4 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)id, 0, NULL); while (true) { 临界区 3、进入临界区 //EnterCriticalSection(&g_cs); 事件 3、等待通知 //WaitForSingleObject(g_hEvent, INFINITE);//使用线程自愿等待,等待到已通知 互斥 3、等待通知 //WaitForSingleObject(g_hEutex, INFINITE);//使用线程自愿等待,等待到已通知 //信号量 3、等待通知 WaitForSingleObject(g_hSemaphore, INFINITE);//使用线程自愿等待,等待到已通知 if (num > 0) { printf("火车站卖出%d张票\n", num); num--; } else { 临界区 4、离开临界区 //LeaveCriticalSection(&g_cs); 事件 4、设置事件通知 //SetEvent(g_hEvent); 互斥 4、重置互斥内核 //ReleaseMutex(g_hEutex); //信号量 4、重置信号量 //第2个参数为重置当前的资源数量 ReleaseSemaphore(g_hSemaphore, 1, NULL); break; } 临界区 4、离开临界区 //LeaveCriticalSection(&g_cs); 事件 4、设置事件通知 //SetEvent(g_hEvent); 互斥 4、重置互斥内核 //ReleaseMutex(g_hEutex); //信号量 4、重置信号量 //第2个参数为重置当前的资源数量 ReleaseSemaphore(g_hSemaphore, 1, NULL); } 临界区 5、释放临界区 //DeleteCriticalSection(&g_cs); 事件 5、关闭事件 //CloseHandle(g_hEvent); 互斥 5、关闭互斥 //CloseHandle(g_hEutex); //信号量 5、关闭信号量 CloseHandle(g_hSemaphore); DWORD threadExitCode1, threadExitCode2, threadExitCode3, threadExitCode4; while (true) { GetExitCodeThread(hThread1, &threadExitCode1);//得到hThread1线程的退出码,把退出码放在threadExitCode1里面 GetExitCodeThread(hThread2, &threadExitCode2); GetExitCodeThread(hThread3, &threadExitCode3); GetExitCodeThread(hThread4, &threadExitCode4); //STILL_ACTIVE 代表线程或者进程仍然活跃 if (threadExitCode1 != STILL_ACTIVE && threadExitCode2 != STILL_ACTIVE && threadExitCode3 != STILL_ACTIVE && threadExitCode4 != STILL_ACTIVE) break; } TerminateThread(hThread1, 0); TerminateThread(hThread2, 0); TerminateThread(hThread3, 0); TerminateThread(hThread4, 0); return 0; }