Win32_Windows线程
一 Windows 线程
1 Windows线程
Windows进程中可以执行代码的实体,Windows系统可以调度的执行代码,一个进程中
至少有一个或多个线程,每个线程是进程的一个任务分支。
2 线程的特点
2.1 每个线程有一个ID
2.2 每个线程有自己的安全属性
2.3 每个线程有自己的内存栈
3 进程和线程多任务
多进程实现的多任务:由于进程地址空间是属于自己私有,内存和资源不能共享。
多线程实现的多任务:由于线程都是位于同一个进程的地址空间,内存和资源可以共享。
4 线程的执行
线程的执行方式采用轮询方式执行。
二 线程的使用
1 定义线程处理函数
DWORD WINAPI ThreadProc(LPVOID lpParameter);//线程参数
2 创建线程
HANDLE CreateThread( LPSECURITY ATTRIBUTES lpThreadAttributes,//安全属性 DWORD dwStackSize,//初始化栈的大小,缺省为0 LPTHREAD_START_ROUTINE lpStartAddress,//线程的函数指针 LPVOID lpParameter,//线程参数 DWORD dwCreationFlags,//创建方式 LPDWORD lpThreadId);//返回线程ID
返回值是创建好的线程句柄
3 结束线程
ExitThread
TerminateThread
4 线程挂起和执行
挂起线程
DWORD SuspendThread(HANDLE hThread);//线程句柄
执行线程
DWORD ResumeThread(HANDLE hThread);
5 等候线程的结束
可以使用 WaitForSingleObject 等候线程的结束
6 关闭线程序句柄
CloseHandle
View Code
1 #include "stdafx.h" 2 #include "conio.h" 3 #include "windows.h" 4 5 DWORD WINAPI ThreadProc1(LPVOID pParam) 6 { 7 DWORD nValue = (DWORD)pParam; 8 for(int nIndex = 0;nIndex < 10;nIndex ++) 9 { 10 printf("Thread Proc1----------%d\n",nValue); 11 Sleep(1000); 12 } 13 return 0; 14 } 15 DWORD WINAPI ThreadProc2(LPVOID pParam) 16 { 17 while(1) 18 { 19 printf("-----------Thread Proc2\n"); 20 Sleep(1000); 21 } 22 } 23 void Create() 24 { 25 DWORD nValue = 100; 26 //创建一个挂起的线程 27 DWORD nThreadID = 0; 28 HANDLE hThread = CreateThread(NULL,0,ThreadProc1, 29 (LPVOID)nValue,CREATE_SUSPENDED,&nThreadID); 30 printf("Thread ID: %d\n",nThreadID); 31 printf("Thread Handle:%p\n",hThread); 32 //执行线程 33 ResumeThread(hThread); 34 //等候线程1结束 35 WaitForSingleObject(hThread,INFINITE); 36 //创建一个立刻执行的线程 37 hThread = CreateThread(NULL,0,ThreadProc2,NULL,0,&nThreadID); 38 printf("Thread 2 ID:%d\n",nThreadID); 39 printf("Thread 2 Handle:%p\n",hThread); 40 //挂起线程 41 //SuspendThread(hThread); 42 CloseHandle(hThread); 43 } 44 45 int main(int argc, char* argv[]) 46 { 47 48 Create(); 49 getch(); 50 return 0; 51 }
二 线程局部存储 Thread Local Storage
1 由于多个线程使用同一个变量,各个线程都对变量进行操作,那么变量的值会被不同线程
操作覆盖。
通常 变量 <-- 线程A
<-- 线程B
TLS 变量 <-- 线程A
变量 <-- 线程B
View Code
1 #include "stdafx.h" 2 #include "conio.h" 3 #include "stdlib.h" 4 #include "windows.h" 5 6 CHAR * g_pszText1 = NULL; //所有子线程共享一个全局变量 7 //使用关键字定义TLS变量 8 __declspec(thread) CHAR * g_pszText2 = NULL; 9 10 void Print( ) 11 { 12 printf( "Text1: %s\n", g_pszText1 ); 13 printf( "Text2: %s\n", g_pszText2 ); 14 } 15 16 DWORD WINAPI PrintProc( LPVOID pParam ) 17 { 18 CHAR * pszText = (CHAR *)pParam; 19 20 g_pszText1 = (CHAR *)malloc( 100 ); 21 memset( g_pszText1, 0, 100 ); 22 strcpy( g_pszText1, pszText ); 23 24 g_pszText2 = (CHAR *)malloc( 100 ); 25 memset( g_pszText2, 0, 100 ); 26 strcpy( g_pszText2, pszText ); 27 28 while( 1 ) 29 { 30 Print( ); 31 Sleep( 1000 ); 32 } 33 return 0; 34 } 35 36 void Create( ) 37 { 38 DWORD dwThread = 0; 39 CHAR szText1[] = "Thread 1----------"; 40 HANDLE hThread = CreateThread( NULL, 41 0, PrintProc, szText1, 0, &dwThread ); 42 43 CHAR szText2[] = "-----Thread 2-----"; 44 hThread = CreateThread( NULL, 45 0, PrintProc, szText2, 0, &dwThread ); 46 47 CHAR szText3[] = "----------Thread 3"; 48 hThread = CreateThread( NULL, 49 0, PrintProc, szText3, 0, &dwThread ); 50 51 getch( ); 52 } 53 54 int main(int argc, char* argv[]) 55 { 56 Create( ); 57 return 0; 58 }
2 TLS 的使用
2.1 使用关键字 _declspec(thread)
_declspec(thread) CHAR* g_pszText2 = NULL
2.2 TLS 相关API
2.2.1 创建TLS索引
DWORD TlsAlloc(VOID)
返回一个TLS索引号
2.2.2 设置值
BOOL TlsSetValue( DWORD dwTlsIndex,//TLS索引 LPVOID lpTlsValue);//保存的值
2.2.3 获取值
LPVOID TlsGetValue(DWORD dwTlsIndex);//TLS索引
返回存放在索引内的值
2.2.4 释放
BOOL TlsFree(DWORD dwTlsIndex);//TLS索引
View Code
1 #include "stdafx.h" 2 #include "stdlib.h" 3 #include "windows.h" 4 5 CHAR * g_pszText = NULL; 6 DWORD g_nTlsIndex = 0; 7 8 void Print( ) 9 { 10 printf( "g_pszText: %s\n", g_pszText ); 11 //从TLS索引中获取值 12 CHAR * pszText = (CHAR *) 13 TlsGetValue( g_nTlsIndex ); 14 printf( "TLS: %s\n", pszText ); 15 } 16 17 DWORD WINAPI PrintProc( LPVOID pParam ) 18 { 19 CHAR * pszText = (CHAR *)pParam; 20 g_pszText = (CHAR *)malloc( 100 ); 21 strcpy( g_pszText, pszText ); 22 //将值保存到TLS索引当中 23 TlsSetValue( g_nTlsIndex, g_pszText ); 24 25 while( 1 ) 26 { 27 Print( ); 28 Sleep( 1000 ); 29 } 30 return 0; 31 } 32 33 void Create( ) 34 { 35 HANDLE hThread = NULL; 36 DWORD nThreadID = 0; 37 38 CHAR szText1[] = "ThreadProc 1----------"; 39 hThread = CreateThread( NULL, 0, 40 PrintProc, szText1, 0, &nThreadID ); 41 42 CHAR szText2[] = "-----ThreadProc 2-----"; 43 hThread = CreateThread( NULL, 0, 44 PrintProc, szText2, 0, &nThreadID ); 45 46 WaitForSingleObject( hThread, INFINITE ); 47 } 48 49 int main(int argc, char* argv[]) 50 { //创建TLS索引号 51 g_nTlsIndex = TlsAlloc( ); 52 //创建线程 53 Create( ); 54 //释放索引 55 TlsFree( g_nTlsIndex ); 56 return 0; 57 }
三 线程同步
1 多线程的问题
A停止 -> B开始 -> B停止 -> A开始
当线程停止会保存寄存器的状态,当线程开始会恢复寄存器的状态
AB线程都使用printf
A线程调用printf时,printf正在输出当中,A挂起,B执行,B线程也调用printf
输出B的数据,画面会出现A的数据输出1部分,然后是B的数据;B挂起,A
执行,A继续输出自己的数据。所以由于多线程的切换产生数据混乱。
2 问题的解决 - 同步机制
2.1 原子锁
2.2 临界区
2.3 事件
2.4 互斥
2.5 信号量
2.6 可等候定时器
3 等候多个内核对象事件
DWORD WaitForMultipleObjects( DWORD nCount,//句柄的数量 CONST HANDLE *lpHandles,//句柄数组 BOOL fWaitAll,//等候方式 DWORD dwMilliseconds);//等候时间
等候方式fWaitAll:
TRUE - 等候每个句柄都有事件,解除阻塞
FALSE - 其中一个句柄有事件解除阻塞
四 原子锁
1 g_nValue++执行
线程A通过寄存器完成加法运算,假设g_nValue正在加到10000时,线程切换到
B,A的寄存器中保存10000数字,B从10000开始加数据,当B加15000时,
线程切换到A,A恢复寄存器的值A会继续从10000开始累加,就将B完成的5000
的加法覆盖
2 原子锁
执行单个指令时,锁定操作不允许其他线程访问
3 用法
InterlockedIncrement ++运算
InterlockedDecrement --运算
InterlockedCompareExchange ?运算
View Code
1 #include "stdafx.h" 2 #include "windows.h" 3 4 LONG g_nValue1 = 0; 5 LONG g_nValue2 = 0; 6 7 DWORD WINAPI InterProc1( LPVOID pParam ) 8 { 9 for( int nIndex=0; nIndex<10000000; nIndex++ ) 10 { 11 g_nValue1++;//普通++ 12 } 13 return 0; 14 } 15 DWORD WINAPI InterProc2( LPVOID pParam ) 16 { 17 for( int nIndex=0; nIndex<10000000; nIndex++ ) 18 { 19 InterlockedIncrement( &g_nValue2 );//原子锁++(lock) 20 } 21 return 0; 22 } 23 24 void Create( ) 25 { 26 DWORD nThreadID = 0;HANDLE hThread[4] = { NULL }; 27 hThread[0] = CreateThread( NULL, 0,InterProc1, NULL, 0, &nThreadID ); 28 29 hThread[1] = CreateThread( NULL, 0,InterProc1, NULL, 0, &nThreadID ); 30 31 hThread[2] = CreateThread( NULL, 0,InterProc2, NULL, 0, &nThreadID ); 32 33 hThread[3] = CreateThread( NULL, 0,InterProc2, NULL, 0, &nThreadID ); 34 35 WaitForMultipleObjects( 4, hThread, TRUE, INFINITE ); 36 37 printf( "Value1=%d Value2=%d\n", 38 g_nValue1, g_nValue2 ); 39 } 40 41 int main(int argc, char* argv[]) 42 { 43 Create( ); 44 return 0; 45 }
五 临界区
1 临界区作用
线程在执行代码时将代码锁定不允许其它线程执行,只有该线程离开后其它才能使用
2 临界区的使用
2.1 初始化临界区
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection);//临界区结构
2.2 临界区加锁
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);//临界区
2.3 临界区解锁
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);//临界区
2.4 释放临界区
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);//临界区
3 和原子锁相比
原子锁是一条语句,临界区可以完成多条语句的锁定
View Code
1 #include "stdafx.h" 2 #include "conio.h" 3 #include "windows.h" 4 5 CRITICAL_SECTION g_cs = {0}; 6 7 void Print() 8 { 9 EnterCriticalSection(&g_cs);//进入临界区 - 加锁 10 printf("Long long long... ...\n"); 11 LeaveCriticalSection(&g_cs);//离开临界区 - 解锁 12 } 13 14 DWORD WINAPI PrintProc(LPVOID pParam) 15 { 16 while(1) 17 { 18 Print(); 19 Sleep(100); 20 } 21 return 0; 22 } 23 24 void Create() 25 { 26 DWORD nThreadID = 0; 27 HANDLE hThread[2] = {0}; 28 hThread[0] = CreateThread(NULL,0,PrintProc,NULL,0,&nThreadID); 29 30 hThread[1] = CreateThread(NULL,0,PrintProc,NULL,0,&nThreadID); 31 32 getch(); 33 } 34 35 int main(int argc, char* argv[]) 36 { 37 InitializeCriticalSection(&g_cs);//初始化临界区 38 Create(); 39 DeleteCriticalSection(&g_cs);//删除临界区 40 return 0; 41 }
六 事件
1 事件
通知的作用,当收到事件时线程可以执行,否则线程将等候事件发生
2 事件的用法
2.1 创建事件
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes,//安全属性 BOOL bManualReset,//重置方式 BOOL bInitialState,//初始化状态 LPCTSTR lpName);
返回创建好的事件句柄
bManualReset - 事件重置方式
TRUE 手动和FALSE自动重置,如果为FALSE,系统在等候到事件后会自动将
事件重置为无信号状态,如果为TRUE,我们必须自己使用ResetEvent重置状态
bInitialState - 初始化状态,TRUE为有信号,FALSE 无信号
2.2 等候事件
WaitForSingleObject / WaitForMultipleObjects
2.3 触发事件
BOOL SetEvent(HANDLE hEvent);//事件句柄
2.4 关闭事件
CloseHandle
2.5 重置事件
BOOL ResetEvent(HANDLE hEvent);//事件句柄
2.6 其它函数
OpenEvent
PulseEvent
View Code
1 #include "stdafx.h" 2 #include "conio.h" 3 #include "windows.h" 4 5 HANDLE g_hEvent = NULL; 6 HANDLE g_hEvent2 = NULL; 7 8 DWORD WINAPI ThreadSend(LPVOID pParam) 9 { 10 while(1) 11 { 12 //触发事件 13 SetEvent(g_hEvent); 14 Sleep(500); 15 SetEvent(g_hEvent2); 16 Sleep(500); 17 } 18 return 0; 19 } 20 21 DWORD WINAPI ThreadRecv(LPVOID pParam) 22 { 23 while(1) 24 { 25 WaitForSingleObject(g_hEvent,INFINITE);//等候事件通知 26 printf("Hello Event:%p\n",g_hEvent); 27 ResetEvent(g_hEvent); 28 } 29 return 0; 30 } 31 32 DWORD WINAPI ThreadRecv2(LPVOID pParam) 33 { 34 while(1) 35 { 36 WaitForSingleObject(g_hEvent2,INFINITE);//等候事件通知 37 printf("Hello Event 2:%p\n",g_hEvent2); 38 ResetEvent(g_hEvent2); 39 } 40 return 0; 41 } 42 43 void Create() 44 { 45 DWORD nThreadID = 0; 46 HANDLE hThread[3] = {NULL}; 47 48 hThread[0] = CreateThread(NULL,0,ThreadSend,NULL,0,&nThreadID); 49 hThread[1] = CreateThread(NULL,0,ThreadRecv,NULL,0,&nThreadID); 50 hThread[2] = CreateThread(NULL,0,ThreadRecv2,NULL,0,&nThreadID); 51 } 52 53 int main(int argc, char* argv[]) 54 { 55 g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);//创建自动事件 56 g_hEvent2 = CreateEvent(NULL,TRUE,FALSE,NULL);//创建手动重置事件 57 Create(); 58 getch(); 59 CloseHandle(g_hEvent);//关闭事件 60 return 0; 61 }
七 互斥
1 互斥量
多个线程同时只能有一个执行
2 互斥量使用
2.1 创建互斥
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes,//安全属性 BOOL bInitialOwner,//初始化的拥有线程 LPCTSTR lpName);//名称
bInitialOwner - TRUE,表示当前创建互斥量的线程拥有互斥,FALSE为不拥有
2.2 等候互斥
WaitForSingleObject
WaitForMultipleObjects
2.3 重置互斥
ReleaseMutex
2.4 关闭互斥
CloseHandle
2.5 使用互斥线程,按照谁先等候谁先拥有互斥量的规则顺序执行
2.6 其它函数
OpenMutex 打开互斥
View Code
1 #include "stdafx.h" 2 #include "conio.h" 3 #include "windows.h" 4 5 HANDLE g_hMutex = NULL; 6 7 DWORD WINAPI ThreadProc1(LPVOID pParam) 8 { 9 while(1) 10 { 11 WaitForSingleObject(g_hMutex,INFINITE);//等候互斥量 12 printf("ThreadProc1----------\n"); 13 Sleep(500); 14 ReleaseMutex(g_hMutex);//释放互斥量 15 } 16 return 0; 17 } 18 DWORD WINAPI ThreadProc2(LPVOID pParam) 19 { 20 while(1) 21 { 22 WaitForSingleObject(g_hMutex,INFINITE);//等候互斥量 23 printf("----------ThreadProc2\n"); 24 Sleep(500); 25 ReleaseMutex(g_hMutex);//释放互斥量 26 } 27 return 0; 28 } 29 30 DWORD WINAPI ThreadProc3(LPVOID pParam) 31 { 32 while(1) 33 { 34 WaitForSingleObject(g_hMutex,INFINITE);//等候互斥量 35 printf("-----ThreadProc3-----\n"); 36 Sleep(500); 37 ReleaseMutex(g_hMutex);//释放互斥量 38 } 39 return 0; 40 } 41 42 void Create() 43 { 44 DWORD nThreadID = 0; 45 HANDLE hThread[3] = {NULL}; 46 47 hThread[0] = CreateThread(NULL,0,ThreadProc1,NULL,0,&nThreadID); 48 hThread[1] = CreateThread(NULL,0,ThreadProc2,NULL,0,&nThreadID); 49 hThread[2] = CreateThread(NULL,0,ThreadProc3,NULL,0,&nThreadID); 50 } 51 52 int main(int argc, char* argv[]) 53 { 54 55 //创建互斥量 56 g_hMutex = CreateMutex(NULL,FALSE,NULL); 57 Create(); 58 getch(); 59 CloseHandle(g_hMutex);//关闭 60 return 0; 61 }
八 信号量
1 信号量
通知作用和事件类似,但是与事件不同,事件只维护一个值 0 或 1 ;信号量维护一个变
量,0 时无信号,大于 0 有信号。
2 信号量的使用
2.1 创建信号量
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//安全属性 LONG lInitialCount,//初始信号量 LONG lMaximumCount,//最大信号量 LPCTSTR lpName);//命名
返回创建好的信号量句柄
2.2 等候信号量
WaitForSingleObject
WaitForMultipleObjects
2.3 释放信号
BOOL ReleaseSemaphore( HANDLE hSemaphore,//信号量句柄 LONG lReleaseCount,//释放信号的数据 LPLONG lpPreviousCount);//释放前的数量
2.4 关闭信号量
CloseHandle
2.5 打开信号量
OpenSemaphore
View Code
1 #include "stdafx.h" 2 #include "conio.h" 3 #include "windows.h" 4 5 HANDLE g_hSemaphore = NULL; 6 7 DWORD WINAPI ThreadSend( LPVOID pParam ) 8 { 9 while( 1 ) 10 { 11 CHAR ch = getch( ); 12 switch( ch ) 13 { 14 case '1': 15 //释放信号 其实是增加可用空位 16 ReleaseSemaphore( g_hSemaphore,1, NULL ); 17 break; 18 case '5': 19 ReleaseSemaphore( g_hSemaphore,5, NULL ); 20 break; 21 } 22 } 23 return 0; 24 } 25 26 DWORD WINAPI ThreadRecv( LPVOID pParam ) 27 { 28 while( 1 ) 29 { //等候信号量的信号 30 WaitForSingleObject( g_hSemaphore, INFINITE ); 31 printf( "Hello Semaphore\n" ); 32 Sleep( 100 ); 33 } 34 return 0; 35 } 36 37 void Create( ) 38 { 39 DWORD nThreadID = 0; 40 HANDLE hThread[2] = { NULL }; 41 hThread[0] = CreateThread( NULL, 0, 42 ThreadSend, NULL, 0, &nThreadID ); 43 hThread[1] = CreateThread( NULL, 0, 44 ThreadRecv, NULL, 0, &nThreadID ); 45 46 WaitForMultipleObjects( 2, hThread, 47 TRUE, INFINITE ); 48 } 49 50 int main(int argc, char* argv[]) 51 { //创建信号量 52 g_hSemaphore = CreateSemaphore( 53 NULL, 3, 10, NULL ); //3表示一开始有3个可用空位 54 Create(); 55 //关闭信号量 56 CloseHandle( g_hSemaphore ); 57 return 0; 58 }
九 可等候定时器
1 可等候定时器
是一个更加精确系统提供的定时器,能够达到100ns级别
2 定时器的使用
2.1 创建定时器
HANDLE CreateWaitableTimer( LPSECURITY_ATTRIBUTES lpTimerAttributes,//安全属性 BOOL bManualReset,//重置方式 LPCTSRT lpTimerName);//命名
返回创建好的定时器句柄
2.2 设置定时器
BOOL SetWaitableTimer( HANDLE hTimer,//定时器句柄 const LARGE_INTEGER *pDueTime,//定时器第一次触发时间,100ns级别 LONG lPeriod,//后续每次触发的间隔,毫秒级别 PTIMERAPCROUTINE pfnCompletionRoutine,//APC处理函数 LPVOID lpArgToCompletionRoutine,//APC参加 BOOL fResume);//休眠标识
pDueTime - 正值表示绝对时间,负值表示相对于现在的时间间隔
lPeriod - 0 定时器不再有后续触发,大于 0 按照间隔触发
pDueTime | lPeriod | lPeriod ...
2.3 等候定时器
WaitForSingleObject
WaitForMultipleObjects
2.4 关闭定时器
CloseHandle
2.5 APC 定时器(异步调用处理)
VOID CALLBACK TimerAPCProc( LPVOID lpArgToCompletionRoutine,//data value DWORD dwTimerLowValue,// timer low value DWORD dwTimerHighValue);//timer high value
2.6 其它
OpenWaitableTimer 打开
CancelWaitableTimer 取消
View Code
1 #include "stdafx.h" 2 3 #define _WIN32_WINNT 0x0400 4 5 #include "windows.h" 6 7 HANDLE g_hTimer = NULL; 8 9 DWORD WINAPI TimerThread( LPVOID pParam ) 10 { 11 while( 1 ) 12 { 13 WaitForSingleObject( g_hTimer, INFINITE ); 14 printf( "Hello Timer\n" ); 15 } 16 17 return 0; 18 } 19 20 21 void Create( ) 22 { //创建定时器 23 g_hTimer = CreateWaitableTimer( NULL, FALSE, NULL ); 24 //设置定时器 25 UINT64 nDueTime = -100000000; 26 SetWaitableTimer( g_hTimer, 27 (PLARGE_INTEGER)&nDueTime, 1000, //nDueTime是第一次触发的时间(纳秒),1000是以后触发间隔(毫秒) 28 NULL, NULL, FALSE ); 29 //创建等候线程 30 DWORD dwThreadID = 0; 31 HANDLE hThread = CreateThread( NULL, 0, 32 TimerThread, NULL, 0, &dwThreadID ); 33 WaitForSingleObject( hThread, INFINITE ); 34 //关闭定时器 35 CloseHandle( g_hTimer ); 36 } 37 38 VOID CALLBACK TimerProc( 39 LPVOID lpArgToCompletionRoutine, 40 DWORD dwTimerLowValue, 41 DWORD dwTimerHighValue ) 42 { 43 printf( "------APC TimerProc--------\n" ); 44 } 45 46 void APCTimer( ) 47 { //创建定时器 48 HANDLE hTimer = CreateWaitableTimer( NULL, FALSE, NULL ); 49 //设置定时器 50 UINT64 nDueTime = -10000000; 51 SetWaitableTimer( hTimer, 52 (PLARGE_INTEGER)&nDueTime, 1000, 53 TimerProc, NULL, FALSE ); 54 // 55 while( 1 ) 56 { 57 SleepEx( -1, TRUE ); //阻塞main()函数,但是waitabletimer的消息还是能执行。说明这是另外一个消息队列 58 } 59 60 //关闭句柄 61 CloseHandle( hTimer ); 62 } 63 64 65 int main(int argc, char* argv[]) 66 { 67 Create( ); 68 //APCTimer( ); 69 70 return 0; 71 }


浙公网安备 33010602011771号