逆向笔记——临界区、互斥体、事件、信号量

逆向笔记——临界区、互斥体、事件、信号量

这些都是什么? 有什么作用,有不同的使用场景,是什么特性/技术让这种场景得以实现的?


临界区、互斥体、事件、信号量是线程同步与互斥的4种方式。只有多个线程同时操作同一公共资源才会产生线程同步与互斥的需求。

多个线程访问引发的线程安全问题

首先来看临界区。

临界区

什么是临界区(what)? 解决了什么场景下的何种问题(why)?如何使用临界区(how)?


what?

当多个线程操作同一个公共资源时,实现多个线程的串行化访问,串行化也就是说控制当某一线程获得操作权时,别的线程就不能获得操作权。只有当前线程的临界区被释放后,别的线程才能有抢占临界区的机会。

why?

临界区是用户态的对象,因此访问数据的速度较快

how?

临界区的使用

1、创建CRITICAL_SECTION

CRITICAL_SECTION cs;				


2、在使用前进行初始化

InitializeCriticalSection(&cs);


3、在函数中使用:

DWORD WINAPI 线程A(PVOID pvParam) 					

{					

      EnterCriticalSection(&cs);					

      //对全局遍历X的操作					
    				
      LeaveCriticalSection(&cs);					

   return(0);					

}		


DWORD WINAPI 线程B(PVOID pvParam)

{					
  EnterCriticalSection(&g_cs);	

 //对全局遍历X的操作					
				
  LeaveCriticalSection(&g_cs);
  
  return(0);					
}	



4、当线程不再试图访问共享资源时,删除CRITICAL_SECTION

VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

临界区的结构
typedef struct _RTL_CRITICAL_SECTION {		
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;		
    LONG LockCount;			//默认值为-1,>=0时表示此临界区被占用
    LONG RecursionCount;	//所有线程已经获得临界区的次数	
    HANDLE OwningThread;    //当前占用临界区的线程的线程标识符,这个ID与GetCurrentThreadId相同    
    HANDLE LockSemaphore;		
    DWORD SpinCount;		
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;		

等待获得临界区的线程数: LockCount-(RecursionCount-1)

实现4个线程对一个全局变量的操作
#include "stdafx.h"		
#include <windows.h>		
CRITICAL_SECTION cs;		
		
DWORD WINAPI ThreadProc1(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		  EnterCriticalSection(&cs);
		  
		  Sleep(1000);
		
		  printf("11111:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		  
		  LeaveCriticalSection(&cs);
		
	}	
	return 0;	
}		
		
DWORD WINAPI ThreadProc2(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		EnterCriticalSection(&cs);
		
		Sleep(1000);
		
		printf("22222:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		
		LeaveCriticalSection(&cs);
		
	}	
		
	return 0;	
}		
		
DWORD WINAPI ThreadProc3(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		EnterCriticalSection(&cs);
		
		Sleep(1000);
		
		printf("33333:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		
		LeaveCriticalSection(&cs);
		
	}	
		
	return 0;	
}		
		
DWORD WINAPI ThreadProc4(LPVOID lpParameter)		
{		
	for(int x=0;x<1000;x++)	
	{	
		EnterCriticalSection(&cs);
		
		Sleep(1000);
		
		printf("44444:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);
		
		LeaveCriticalSection(&cs);
		
	}	
		
	return 0;	
}		
		
		
int main(int argc, char* argv[])		
{		
	InitializeCriticalSection(&cs);	
		
	//printf("主线程:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);	
		
	//创建一个新的线程	
	HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, 	
		NULL, 0, NULL);
		
	//创建一个新的线程	
	HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, 	
		NULL, 0, NULL);
		
	//创建一个新的线程	
	HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, 	
		NULL, 0, NULL);
		
	//创建一个新的线程	
	HANDLE hThread4 = ::CreateThread(NULL, 0, ThreadProc4, 	
		NULL, 0, NULL);
		
	//如果不在其他的地方引用它 关闭句柄	
	::CloseHandle(hThread1);	
	::CloseHandle(hThread2);	
	::CloseHandle(hThread3);	
	::CloseHandle(hThread4);	
		
	Sleep(1000*60*60);	
		
	return 0;	
}		

实现生产者消费者模型(多线程的同步)

CRITICAL_SECTION g_cs;		
int g_Max = 10;		
int g_Number = 0;                      		
//生产者线程函数  		
DWORD WINAPI ThreadProduct(LPVOID pM)  		
{  		
    for (int i = 0; i < g_Max; i++)		
    {  		
        //互斥的访问缓冲区  		
        EnterCriticalSection(&g_cs); 		
		g_Number = 1; 
		DWORD id = GetCurrentThreadId();
		printf("生产者%d将数据%d放入缓冲区\n",id, g_Number); 
        LeaveCriticalSection(&g_cs); 		
		
    }  		
    return 0;  		
}  		
//消费者线程函数		
DWORD WINAPI ThreadConsumer(LPVOID pM)  		
{  		
    for (int i = 0; i < g_Max; i++)		
    {  		
        //互斥的访问缓冲区  		
        EnterCriticalSection(&g_cs);  		
		g_Number = 0; 
		DWORD id = GetCurrentThreadId();
		printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number); 
	LeaveCriticalSection(&g_cs); 	
    }  		
    return 0;  		
}  		
		
int main(int argc, char* argv[])		
{		
	InitializeCriticalSection(&g_cs);	
		
		
    HANDLE hThread[2]; 		
		
    hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL); 		
	hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);	
		
    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  		
    CloseHandle(hThread[0]);  		
    CloseHandle(hThread[1]);		
		
	//销毁 	
    DeleteCriticalSection(&g_cs);  		
		
		
	return 0;	
}		

临界区作用域的选定原则

1、当多个线程同时对一个全局资源进行遍历操作时,临界区应作用在循环体内,否则效率很慢,因为要等第一个抢占临界区的线程执行完循环体后,第二个线程才能抢占临界区。

2、不是操作全局资源的代码不应该放在临界区内,同样会影响效率。

3、多个操作公共资源线程都应该被临界区控制,否则还是会引起线程安全问题。

4、当有N公共资源时,应该创建N个临界区,即控制公共资源的操作应原子化。

互斥体同临界区一样,可以实现线程的控制,那 既生“临界区”? 何生“互斥体”?

互斥体

什么是互斥体(what)? 相比临界区而言,为啥要使用互斥体(why)? 如何使用互斥体(how)?

what?

互斥体具有与临界区同样的线程控制机制,但除此之外,互斥体还能够实现跨进程的线程控制,这得益于互斥体是内核对象。

why?

1、临界区只能用于单个进程间的线程控制.

2、互斥体可以设定等待超时,但临界区不能.

3、线程意外终结时,Mutex可以避免无限等待.

4、Mutex效率没有临界区高.

how?

互斥体的操作步骤

1、创建互斥体

HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ");

2、打开互斥体

HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");

3、使线程进入等待状态,直到一个特定的内核对象变为已通知

查看一个对象是否进入已通知状态

DWORD dwCode = ::WaitForSingleObject(g_hMutex,INFINITE);

查看多个对象是否进入已通知状态

DWORD dwCode = ::WaitForMultipleObjects(2, hArray,FALSE,INFINITE);

4、释放互斥体

ReleaseMutex(g_hMutex);

几种结构的解释

0、Linux内核中的mutex的结构(定义在include/linux/metex.h)

struct mutex {
	atomic_long_t		owner; //锁定的对象
	spinlock_t		wait_lock; //等待锁定
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	struct optimistic_spin_queue osq; /* Spinner MCS lock 自旋MCS锁*/
#endif
	struct list_head	wait_list; //等待队列
#ifdef CONFIG_DEBUG_MUTEXES
	void			*magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};

1、WaitForSingleObject查看单个对象是否进入已通知的状态

DWORD WaitForSingleObject(				
  HANDLE hHandle,        // handle to object内核句柄			
  DWORD dwMilliseconds   // time-out interval	超时时间			
);				

返回值:

WAIT_OBJECT_0(0) 等待对象变为已通知

WAIT_TIMEOUT(0x102) 超时

代码

DWORD WINAPI ThreadProc1(LPVOID lpParameter)				
{				
	for(int i=0;i<5;i++)			
	{			
		printf("+++++++++\n");		
		Sleep(1000);		
	}			
	return 0;			
}				
				
int main(int argc, char* argv[])				
{				
				
	//创建一个新的线程			
	HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, 			
		NULL, 0, NULL);		
				
	DWORD dwCode = ::WaitForSingleObject(hThread1, INFINITE);			
				
	MessageBox(0,0,0,0);			
				
	return 0;			
}				

2、WaitForMultipleObjects 查看多个对象是否进入已通知的状态

DWORD WaitForMultipleObjects(					
  DWORD nCount,             // number of handles in array要查看的内核的数量				
  CONST HANDLE *lpHandles,  // object-handle array	内核对象数组				
  BOOL bWaitAll,            // wait option等待类型,true:等待所有变为已通知  false:等待一个变为已通知			
  DWORD dwMilliseconds      // time-out interval超时时间 INFINITE 表示一直等待				
);					

返回值:

bWaitAll为TRUE时,返回WAIT_OBJECT_0(0) 代码所以内核对象都变成已通知

bWaitAll为FALSE时,返回最先变成已通知的内核对象在数组中的索引

WAIT_TIMEOUT(0x102) 超时

特别说明:

1、内核对象中的每种对象都可以说是处于已通知或未通知的状态之中

2、这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的

3、当线程正在运行的时候,线程内核对象处于未通知状态

4、当线程终止运行的时候,它就变为已通知状态

5、在内核中就是个BOOL值,运行时FALSE 结束TRUE

代码

DWORD WINAPI ThreadProc1(LPVOID lpParameter)					
{					
	for(int i=0;i<5;i++)				
	{				
		printf("+++++++++\n");			
		Sleep(1000);			
	}				
	return 0;				
}					
					
DWORD WINAPI ThreadProc2(LPVOID lpParameter)					
{					
	for(int i=0;i<3;i++)				
	{				
		printf("---------\n");			
		Sleep(1000);			
	}				
					
	return 0;				
}					
					
					
int main(int argc, char* argv[])					
{					
					
	HANDLE hArray[2];				
					
	//创建一个新的线程				
	HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, 				
		NULL, 0, NULL);			
					
	//创建一个新的线程				
	HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, 				
		NULL, 0, NULL);			
					
	hArray[0] = hThread1;				
	hArray[1] = hThread2;				
					
	DWORD dwCode = ::WaitForMultipleObjects(2, hArray,FALSE,INFINITE);				
					
	MessageBox(0,0,0,0);				
					
	return 0;				
}					

实现生产者消费者模型(线程同步)

//互斥体		
HANDLE hMutex;		
int g_Max = 10;		
int g_Number = 0;                      		
//生产者线程函数  		
DWORD WINAPI ThreadProduct(LPVOID pM)  		
{  		
    for (int i = 0; i < g_Max; i++)		
    {  		
        //互斥的访问缓冲区  		
        WaitForSingleObject(hMutex,INFINITE);		
		g_Number = 1; 
		DWORD id = GetCurrentThreadId();
		printf("生产者%d将数据%d放入缓冲区\n",id, g_Number); 
        ReleaseMutex(hMutex);		
    }  		
    return 0;  		
}  		
//消费者线程函数		
DWORD WINAPI ThreadConsumer(LPVOID pM)  		
{  		
    for (int i = 0; i < g_Max; i++)		
    {  		
        //互斥的访问缓冲区  		
        WaitForSingleObject(hMutex,INFINITE);		
		g_Number = 0; 
		DWORD id = GetCurrentThreadId();
		printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number); 
		ReleaseMutex(hMutex);
    }  		
    return 0;  		
}  		
		
int main(int argc, char* argv[])		
{		
	//创建一个互斥体	
	hMutex =  CreateMutex(NULL,FALSE,NULL);	
		
		
    HANDLE hThread[2]; 		
		
    hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL); 		
	hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);	
		
    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  		
    CloseHandle(hThread[0]);  		
    CloseHandle(hThread[1]);		
		
	//销毁 	
    CloseHandle(hMutex);		
		
		
	return 0;	
}		

事件也是内核对象,同为内核对象的互斥体与事件的原理及应用场景有啥不同呢?

事件

什么是事件(what)? 为什么要使用事件(why)? 事件与互斥体有何不同? 如何使用事件?

what?

事件是内核对象,用于通知线程某些事情已发生,从而启动后继任务的开始。

why?

可以在创建事件对象时,ResetEvent将事件对象标记为未通知,从而将线程锁定,这种方式相较于互斥体,更为灵活。

how?

事件的使用步骤

1、创建事件内核对象

HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");						

2、获取事件内核对象

HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");						

3、销毁事件内核对象

BOOL CloseHandle(HANDLE hobj);			

注意·: 当没有其他程序引用时,系统会销毁内核对象

​ 内核对象的生命周期可能比创建它的对象要长,也就是说 即便关闭了句柄,创建的对象也会在内存空间存在。

相关结构介绍

1、OpenEvent

HANDLE OpenEvent(					
  DWORD dwDesiredAccess,  // access					
  BOOL bInheritHandle,    // inheritance option					
  LPCTSTR lpName          // object name					
);					

2、CreateEvent

HANDLE CreateEvent(				
  LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 NULL时为系统默认				
  BOOL bManualReset,                       // TRUE 通过调用ResetEvent将事件对象标记为未通知,如果为FALSE,与互斥体的功能就是一样的。				
  BOOL bInitialState,                      // TRUE 已通知状态  FALSE未通知状态				
  LPCTSTR lpName                           // 对象名称 以NULL结尾的字符串				
);				

代码实现

以只读的方式进程线程的控制

HANDLE g_hEvent;				
				
HWND hEdit1;				
HWND hEdit2;				
HWND hEdit3;				
HWND hEdit4;				
HANDLE hThread1;				
HANDLE hThread2;				
HANDLE hThread3;				
HANDLE hThread4;				
				
DWORD WINAPI ThreadProc1(LPVOID lpParameter)				
{				
	//创建事件			
	//默认安全属性  手动设置未通知状态(TRUE)  初始状态未通知 没有名字 			
	g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);			
	HANDLE hThread[3];			
	//创建3个线程			
	hThread[0] = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);			
	hThread[1] = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);			
	hThread[2] = ::CreateThread(NULL, 0, ThreadProc4, NULL, 0, NULL);			
				
	//设置文本框的值			
	SetWindowText(hEdit1,"1000");			
				
	//设置事件为已通知			
	SetEvent(g_hEvent);			
				
	//等待线程结束 销毁内核对象			
	WaitForMultipleObjects(3, hThread, TRUE, INFINITE);  			
	CloseHandle(hThread[0]);  			
	CloseHandle(hThread[1]);			
	CloseHandle(hThread[2]);			
	CloseHandle(g_hEvent);  			
				
	return 0;			
}				
				
DWORD WINAPI ThreadProc2(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10] = {0};			
				
	//当事件变成已通知时 			
	WaitForSingleObject(g_hEvent, INFINITE);			
				
	//读取内容			
	GetWindowText(hEdit1,szBuffer,10);			
				
	SetWindowText(hEdit2,szBuffer);			
				
	return 0;			
}				
DWORD WINAPI ThreadProc3(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10] = {0};			
				
	//当事件变成已通知时 			
	WaitForSingleObject(g_hEvent, INFINITE);			
				
	//读取内容			
	GetWindowText(hEdit1,szBuffer,10);			
				
	SetWindowText(hEdit3,szBuffer);			
				
	return 0;			
}				
DWORD WINAPI ThreadProc4(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10] = {0};			
				
	//当事件变成已通知时 			
	WaitForSingleObject(g_hEvent, INFINITE);			
				
	//读取内容			
	GetWindowText(hEdit1,szBuffer,10);			
				
	SetWindowText(hEdit4,szBuffer);			
				
	return 0;			
}				

实现生产者消费者模型(线程的同步)

//事件和临界区		
HANDLE g_hSet, g_hClear;		
int g_Max = 10;		
int g_Number = 0;		
                   		
//生产者线程函数  		
DWORD WINAPI ThreadProduct(LPVOID pM)  		
{  		
    for (int i = 0; i < g_Max; i++)		
    {  		
        WaitForSingleObject(g_hSet, INFINITE);  		
		g_Number = 1; 
		DWORD id = GetCurrentThreadId();
		printf("生产者%d将数据%d放入缓冲区\n",id, g_Number); 
        SetEvent(g_hClear);   		
    }  		
    return 0;  		
}  		
//消费者线程函数		
DWORD WINAPI ThreadConsumer(LPVOID pM)  		
{  		
    for (int i = 0; i < g_Max; i++)		
    {  		
        WaitForSingleObject(g_hClear, INFINITE);  		
		g_Number = 0; 
		DWORD id = GetCurrentThreadId();
		printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number); 
        SetEvent(g_hSet);   		
    }  		
    return 0;  		
}  		
		
int main(int argc, char* argv[])		
{		
		
    HANDLE hThread[2]; 		
		
	g_hSet = CreateEvent(NULL, FALSE, TRUE, NULL);  	
    g_hClear = CreateEvent(NULL, FALSE, FALSE, NULL); 		
		
    hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL); 		
	hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);	
		
    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  		
    CloseHandle(hThread[0]);  		
    CloseHandle(hThread[1]);		
		
	//销毁 	
	CloseHandle(g_hSet);  	
    CloseHandle(g_hClear);  		
		
	return 0;	
}		

在临界区->互斥体->事件中,将效率、灵活度、时间控制都考虑进去了,但信号量又是“考虑”啥了呢?

信号量

什么是信号量(what)? 为何要有信号量这种控制方式(why)? 如何使用信号量(how)?

what?

信号量允许多个线程同时控制资源,并指出可同时访问共享资源的线程最大数目。

why?

信号量是为线程同时使用共享资源的线程数目而设计的。

how?

1、创建信号量

HANDLE hSemaphore;	
hSemaphore = CreateSemaphore(NULL,0,3,NULL);			

2、打开信号量

HANDLE OpenSemaphore(
  DWORD dwDesiredAccess,  // access flag
  BOOL bInheritHandle,    // inherit flag
  LPCTSTR lpName          // pointer to semaphore-object name
);

3、递增信号量的当前资源计数

4、清理与销毁信号量

::CloseHandle(hSemaphore);

几种结构体

1、CreateSemaphore 创建信号量

HANDLE CreateSemaphore(		
		
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,		//安全控制,一般为NULL
		
  LONG lInitialCount,	// 初始资源数量。 0时不发送信号
		
  LONG lMaximumCount,   //最大并发数
		
  LPCTSTR lpName		//信号量的名字
);

2、打开信号量

HANDLE OpenSemaphore(		
		
  DWORD dwDesiredAccess,	//访问权限 ,对一般传入 SEMAPHOR_ALL_ACCESS	
		
  BOOL bInheritHandle,  //信号量句柄继承性,一般传入FALSE即可
		
  LPCTSTR lpName //信号量名称
		
);


3、ReleaseSemaphore 释放信号量

BOOL ReleaseSemaphore(		
		
  HANDLE hSemaphore,	//信号量句柄	
		
  LONG lReleaseCount,   //参数增加的个数
		
  LPLONG lpPreviousCount  //返回当前资源数量的原始值,设为NULL表示不需要传出 		
		
);	

练习 :一个抢红包的例子

功能界面

使用临界区实现

// RedEnvLuDraw.cpp : Defines the entry point for the application.
//

#include "stdafx.h"	
#include "resource.h"		
#include <windows.h>
#include <stdio.h>

BOOL CALLBACK DialogProc(							
						 HWND hwndDlg,  // handle to dialog box	
						 UINT uMsg,     // message	
						 WPARAM wParam, // first message parameter	
						 LPARAM lParam  // second message parameter	

						 );
DWORD WINAPI ThreadProc1(LPVOID lpParam);
DWORD WINAPI ThreadProc2(LPVOID lpParam);
DWORD WINAPI ThreadProc3(LPVOID lpParam);
DWORD WINAPI ThreadProc(LPVOID lpParam);//临界区
DWORD WINAPI ThreadOP(HWND hWnd);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 	// TODO: Place code here.
	DialogBox( hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);	
	return 0;
}



/////////////////////////////////////////////////////////////////////////
HANDLE redEnvTotal;
HWND hwndDlg;
CRITICAL_SECTION cs;
	

BOOL CALLBACK DialogProc(							
						 HWND hdDlg,  // handle to dialog box	
						 UINT uMsg,     // message	
						 WPARAM wParam, // first message parameter	
						 LPARAM lParam  // second message parameter	
						 )	
{							

	hwndDlg = hdDlg;
	HANDLE hThread;
	switch(uMsg)						
	{						
	case  WM_INITDIALOG :

		TCHAR szBuffer[0x50];
		wsprintf(szBuffer,"%d",1000);										
		SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_TOTAL),szBuffer);		
		
		wsprintf(szBuffer,"%d",0);										
		SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL1),szBuffer);		
		
		wsprintf(szBuffer,"%d",0);										
		SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL2),szBuffer);		
		
		wsprintf(szBuffer,"%d",0);										
		SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL3),szBuffer);		
	
		return TRUE ;					
							
	case  WM_COMMAND :						
							
		switch (LOWORD (wParam))					
		{					
		case   IDDRAW :		


			hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); 
			
			TCHAR szBuffer[0x50];
			//红包金额总数为szBuffer
			GetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_TOTAL),szBuffer,0x50);	
			//获得控制权后,szBuffer-50
			int res;
			sscanf( szBuffer, "%d", &res );	//字符转数字	
			if(res<50)
			{
				//终止所有线程,并关闭
				::ExitThread(3);
				return 0;
			}
            // 关闭句柄                
            ::CloseHandle(hThread);            

			return TRUE;
			
		case   IDCANCEL :		
			EndDialog(hwndDlg, 0);	
			return TRUE;
		}					
		break ;					
    }							
							
	return FALSE ;						
}
DWORD WINAPI ThreadProc(LPVOID lpParam)//临界区
{
	InitializeCriticalSection(&cs);
	//创建一个新的线程	
	HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, 	
				NULL, 0, NULL);
				
	//创建一个新的线程	
	HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, 	
				NULL, 0, NULL);

	//创建一个新的线程	
	HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, 	
				NULL, 0, NULL);
	return 0;
}
DWORD WINAPI ThreadProc1(LPVOID lpParam)//临界区
{
	ThreadOP(GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL1));
	return 0;
}


DWORD WINAPI ThreadProc2(LPVOID lpParam)//临界区
{
	ThreadOP(GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL2));
	return 0;
}


DWORD WINAPI ThreadProc3(LPVOID lpParam)//临界区
{
	ThreadOP(GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL3));
	return 0;
}

DWORD WINAPI ThreadOP(HWND hWnd)
{
	EnterCriticalSection(&cs);//获得令牌

	TCHAR szBuffer[0x50];
	//红包金额总数为szBuffer
	GetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_TOTAL),szBuffer,0x50);	
	//获得控制权后,szBuffer-50
	int res;
	sscanf( szBuffer, "%d", &res );	//字符转数字	
	if(res<50)
	{
		//终止所有线程,并关闭
		return 0;
	}
	res = res-50;

	//写回红包编辑框T 和 1
	wsprintf(szBuffer,"%d",res);										
	SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_TOTAL),szBuffer);

	GetWindowText(hWnd,szBuffer,0x50);		
	sscanf( szBuffer, "%d", &res );	//字符转数字
	res = res+50;
	wsprintf(szBuffer,"%d",res);										
	SetWindowText(hWnd,szBuffer);

	
	LeaveCriticalSection(&cs);
	Sleep(50);
	return 0;
}

使用互斥体实现


#include "stdafx.h"
#include "resource.h"
#include<windows.h>
#include<stdio.h>


HWND redEnv;
HWND lucky1;
HWND lucky2;
HWND lucky3;
 
HANDLE g_hMutex; //互斥体
DWORD num; //现有红包金额的总数
 
//lucky1
DWORD WINAPI ThreadProc1(                
             LPVOID lpParameter          
             ){
    while(num > 50){
        WaitForSingleObject(g_hMutex,INFINITE);    //等待互斥体
 
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(redEnv, szBuffer, 10);
 
        TCHAR lucky1Buffer[10];
        memset(lucky1Buffer, 0, 10);
        GetWindowText(lucky1, lucky1Buffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD lucky1Num;
        sscanf(lucky1Buffer, "%d", &lucky1Num);

        if(num > 50){
            memset(szBuffer, 0, 10);
            memset(lucky1Buffer, 0, 10);
            
			num = num-50;
            sprintf(szBuffer, "%d", num);

			lucky1Num = lucky1Num+50;
            sprintf(lucky1Buffer, "%d", lucky1Num);
            SetWindowText(redEnv, szBuffer);
            SetWindowText(lucky1, lucky1Buffer);
        }
 
        ReleaseMutex(g_hMutex);    //释放互斥体
        Sleep(500);
    }
    return 0;
}
 
//lucky2
DWORD WINAPI ThreadProc2(                
             LPVOID lpParameter          
             ){
    while(num > 50){
        WaitForSingleObject(g_hMutex,INFINITE);    //等待互斥体
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(redEnv, szBuffer, 10);
 
        TCHAR lucky2Buffer[10];
        memset(lucky2Buffer, 0, 10);
        GetWindowText(lucky2, lucky2Buffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD lucky2Num;
        sscanf(lucky2Buffer, "%d", &lucky2Num);
        if(num > 50){
            memset(szBuffer, 0, 10);
            memset(lucky2Buffer, 0, 10);
            
			num = num-50;
            sprintf(szBuffer, "%d", num);

			lucky2Num = lucky2Num+50;
            sprintf(lucky2Buffer, "%d", lucky2Num);
            SetWindowText(redEnv, szBuffer);
            SetWindowText(lucky2, lucky2Buffer);
        }
        ReleaseMutex(g_hMutex);    //释放互斥体
        Sleep(500);
    }
    return 0;
}
 
//lucky3
DWORD WINAPI ThreadProc3(                
             LPVOID lpParameter          
             ){
    while(num > 50){
        WaitForSingleObject(g_hMutex,INFINITE);    //等待互斥体
 
        //获取文本框内容
        TCHAR szBuffer[10];
        memset(szBuffer, 0, 10);
        GetWindowText(redEnv, szBuffer, 10);
 
        TCHAR lucky3Buffer[10];
        memset(lucky3Buffer, 0, 10);
        GetWindowText(lucky3, lucky3Buffer, 10);
    
        //字符串转数字
        sscanf(szBuffer, "%d", &num);
        DWORD lucky3Num;
        sscanf(lucky3Buffer, "%d", &lucky3Num);
        if(num > 50){
            memset(szBuffer, 0, 10);
            memset(lucky3Buffer, 0, 10);
            
			num = num-50;
            sprintf(szBuffer, "%d", num);

			lucky3Num = lucky3Num+50;
            sprintf(lucky3Buffer, "%d", lucky3Num);
            SetWindowText(redEnv, szBuffer);
            SetWindowText(lucky3, lucky3Buffer);
        }
        ReleaseMutex(g_hMutex);    //释放互斥体
        Sleep(500);
    }
    return 0;
}
 
 
//线程函数
DWORD WINAPI ThreadProc(                
             LPVOID lpParameter   // 给线程传递参数       
             ){
    g_hMutex = CreateMutex(NULL,FALSE, "XYZ");    //创建互斥体
    
    HANDLE hArray[3];
    //创建线程
    HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);                                   
    HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);                                        
    HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);
    
    //等待所有线程执行完后关闭互斥体
    hArray[0] = hThread1;                        
    hArray[1] = hThread2;
    hArray[2] = hThread3;
    DWORD dwCode = ::WaitForMultipleObjects(3, hArray,FALSE,INFINITE);
    ::CloseHandle(g_hMutex);
 
    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 :
        redEnv = GetDlgItem(hwndDlg, IDC_EDIT_TOTAL);
        lucky1 = GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL1);
        lucky2 = GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL2);
        lucky3 = GetDlgItem(hwndDlg, IDC_EDIT_PARTIAL3);
 
		SetWindowText(redEnv, TEXT("1000"));
        SetWindowText(lucky1, TEXT("0"));
        SetWindowText(lucky2, TEXT("0"));
        SetWindowText(lucky3, TEXT("0"));
        return TRUE;     
                                    
    case  WM_COMMAND :                                                                   
        switch (LOWORD (wParam))                            
        {
        case IDDRAW:  
            //获取文本框内容
            TCHAR szBuffer[10];
            memset(szBuffer, 0, 10);
            GetWindowText(redEnv, szBuffer, 10);
            //字符串转数字
            sscanf(szBuffer, "%d", &num);
 
            //清空输入框
			SetWindowText(redEnv, TEXT("1000"));
            SetWindowText(lucky1, TEXT("0"));
            SetWindowText(lucky2, TEXT("0"));
            SetWindowText(lucky3, TEXT("0"));
 
            HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);        
            // 关闭句柄 ,防止对象泄漏            
            ::CloseHandle(hThread);            
            return TRUE;  
 
        return FALSE;    
        }                            
        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_DIALOG1), NULL, DialogProc);    
    
    return 0;
}

使用信号量实现

#include "stdafx.h"				
#include "resource.h"				
HANDLE hSemaphore;				
HANDLE hThread[3];				
HWND hEditSet;				
HWND hEdit1;				
HWND hEdit2;				
HWND hEdit3;				
DWORD WINAPI ThreadProc1(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10];			
	DWORD dwTimmer=0;			
	WaitForSingleObject(hSemaphore, INFINITE);			
	while(dwTimmer<100)			
	{			
		Sleep(100);		
		memset(szBuffer,0,10);		
		GetWindowText(hEdit1,szBuffer,10);		
		sscanf( szBuffer, "%d", &dwTimmer );		
		dwTimmer++;		
		memset(szBuffer,0,10);		
		sprintf(szBuffer,"%d",dwTimmer);		
		SetWindowText(hEdit1,szBuffer);		
	}			
	ReleaseSemaphore(hSemaphore, 1, NULL);			
	return 0;			
}				
DWORD WINAPI ThreadProc2(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10];			
	DWORD dwTimmer=0;			
	WaitForSingleObject(hSemaphore, INFINITE);			
	while(dwTimmer<100)			
	{			
		Sleep(100);		
		memset(szBuffer,0,10);		
		GetWindowText(hEdit2,szBuffer,10);		
		sscanf( szBuffer, "%d", &dwTimmer );		
		dwTimmer++;		
		memset(szBuffer,0,10);		
		sprintf(szBuffer,"%d",dwTimmer);		
		SetWindowText(hEdit2,szBuffer);		
	}			
	ReleaseSemaphore(hSemaphore, 1, NULL);			
	return 0;			
}				
DWORD WINAPI ThreadProc3(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10];			
	DWORD dwTimmer=0;			
	WaitForSingleObject(hSemaphore, INFINITE);			
	while(dwTimmer<100)			
	{			
		Sleep(100);		
		memset(szBuffer,0,10);		
		GetWindowText(hEdit3,szBuffer,10);		
		sscanf( szBuffer, "%d", &dwTimmer );		
		dwTimmer++;		
		memset(szBuffer,0,10);		
		sprintf(szBuffer,"%d",dwTimmer);		
		SetWindowText(hEdit3,szBuffer);		
	}			
	ReleaseSemaphore(hSemaphore, 1, NULL);			
	return 0;			
}				
DWORD WINAPI ThreadBegin(LPVOID lpParameter)				
{				
	TCHAR szBuffer[10];			
	DWORD dwMoney=0;			
				
				
	hSemaphore = CreateSemaphore(NULL,0,3,NULL);			
				
	hThread[0] = ::CreateThread(NULL, 0, ThreadProc1,NULL, 0, NULL);			
	hThread[1] = ::CreateThread(NULL, 0, ThreadProc2,NULL, 0, NULL);			
	hThread[2] = ::CreateThread(NULL, 0, ThreadProc3,NULL, 0, NULL);			
				
	//开始准备红包			
	while(dwMoney<1000)			
	{			
		memset(szBuffer,0,10);		
		GetWindowText(hEditSet,szBuffer,10);		
		sscanf( szBuffer, "%d", &dwMoney );		
		dwMoney++;		
		memset(szBuffer,0,10);		
		sprintf(szBuffer,"%d",dwMoney);		
		SetWindowText(hEditSet,szBuffer);		
	}			
	ReleaseSemaphore(hSemaphore, 2, NULL);			
				
	::WaitForMultipleObjects(3, hThread,TRUE,INFINITE);			
	::CloseHandle(hSemaphore);			
				
	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:			
		{		
			hEditSet = GetDlgItem(hDlg,IDC_EDIT_SET);	
			hEdit1 = GetDlgItem(hDlg,IDC_EDIT_1);	
			hEdit2 = GetDlgItem(hDlg,IDC_EDIT_2);	
			hEdit3 = GetDlgItem(hDlg,IDC_EDIT_3);	
				
			SetWindowText(hEditSet,"0");	
			SetWindowText(hEdit1,"0");	
			SetWindowText(hEdit2,"0");	
			SetWindowText(hEdit3,"0");	
				
			break;	
		}		
	case WM_COMMAND:			
				
		switch (LOWORD (wParam))		
		{		
		case IDC_BUTTON_BEGIN:		
			{	
				CreateThread(NULL, 0, ThreadBegin,NULL, 0, NULL);
				
				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;			
}				
posted @ 2020-04-19 10:13  程序媛墨禾  阅读(643)  评论(0编辑  收藏  举报