• 零基础逆向工程35_Win32_09_临界区_CRITICAL_SECTION结构


    1 引入

    为什么会存在临界区这中机制呢?是为多线程同时访问全局变量而引入的。也就是上一篇帖子的末尾流出的问题程序的解决办法。

    看懂了上面的,那么我们再罗嗦总结一下:
    1.多线程访问全局变量时,存在线程安全问题。
    2.局部变量不存在线程安全问题。

    2 临界区的使用

    2.1 创建CRITICAL_SECTION:

    CRITICAL_SECTION cs;
    

    2.2 在使用前进行初始化

    InitializeCriticalSection(&cs);		
    

    2.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);		
    }		
    

    2.4 删除CRITICAL_SECTION

    VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);//当线程不再试图访问共享资源时		
    

    3 CRITICAL_SECTION

    3.1 结构

    typedef struct _RTL_CRITICAL_SECTION {
        PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
        LONG LockCount;
        LONG RecursionCount;
        HANDLE OwningThread;       
        HANDLE LockSemaphore;
        DWORD SpinCount;
    } RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
    

    LockCount:
    它被初始化为数值 -1
    此数值等于或大于 0 时,表示此临界区被占用

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

    RecursionCount:
    此字段包含所有者线程已经获得该临界区的次数

    OwningThread:
    此字段包含当前占用此临界区的线程的线程标识符
    此线程 ID 与GetCurrentThreadId 所返回的 ID 相同

    3.2 测试代码

    #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
    ",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
    ",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
    ",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
    ",cs.LockCount,cs.RecursionCount,cs.OwningThread);
    		
    		LeaveCriticalSection(&cs);
    		
    	}	
    		
    	return 0;	
    }		
    		
    		
    int main(int argc, char* argv[])		
    {		
    	InitializeCriticalSection(&cs);	
    		
    	//printf("主线程:%x %x %x
    ",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;	
    }
    

    4 使用时容易出错和造成程序不稳定的地方

    4.1 怎样使用是合理的

    场景1:

    //1. 错误
    DWORD WINAPI 线程A(PVOID pvParam) 		
    {		
       EnterCriticalSection(&cs);
       while(g_nIndex < MAX_TIMES) 		
       {		
          //对全局遍历X的操作				
       }		
       LeaveCriticalSection(&cs);
       return(0);		
    }
    		
    		
    DWORD WINAPI 线程B(PVOID pvParam) 		
    {		
       EnterCriticalSection(&cs);
       while(g_nIndex < MAX_TIMES) 		
       {		
          //对全局遍历X的操作				
       }		
       LeaveCriticalSection(&cs);
       return(0);		
    }	
    
    //2. 正确
    DWORD WINAPI 线程A(PVOID pvParam) 		
    {		
       		
       while(g_nIndex < MAX_TIMES) 		
       {		
          EnterCriticalSection(&cs);		
          //对全局遍历X的操作		
          LeaveCriticalSection(&cs);		
       }		
       		
       return(0);		
    		
    		
    		
    DWORD WINAPI 线程B(PVOID pvParam) 		
    {		
       		
       while(g_nIndex < MAX_TIMES) 		
       {		
          EnterCriticalSection(&cs);		
          //对全局遍历X的操作		
          LeaveCriticalSection(&cs);		
       }		
       		
       return(0);		
    }		
    

    场景2:

    //1. 错误
    DWORD WINAPI 线程A(PVOID pvParam) 		
    {		
       EnterCriticalSection(&cs);		
          //代码xxxxxx		
          //代码xxxxxx		
    		
          //对全局遍历X的操作		
          		
          //代码xxxxxx		
          //代码xxxxxx		
       LeaveCriticalSection(&cs);		
    }		
    		
    		
    DWORD WINAPI 线程B(PVOID pvParam) 		
    {		
       EnterCriticalSection(&cs);		
          //代码xxxxxx		
          //代码xxxxxx		
    		
          //对全局遍历X的操作		
          		
          //代码xxxxxx		
          //代码xxxxxx		
       LeaveCriticalSection(&cs);		
    }
    
    //2. 正确
    DWORD WINAPI 线程A(PVOID pvParam) 		
    {				
          //代码xxxxxx		
          //代码xxxxxx		
    	EnterCriticalSection(&cs);	
          //对全局遍历X的操作		
        LeaveCriticalSection(&cs);
          //代码xxxxxx		
          //代码xxxxxx		
    }
    
    DWORD WINAPI 线程B(PVOID pvParam) 		
    {				
          //代码xxxxxx		
          //代码xxxxxx		
    	EnterCriticalSection(&cs);	
          //对全局遍历X的操作		
        LeaveCriticalSection(&cs);
          //代码xxxxxx		
          //代码xxxxxx		
    }
    

    场景3:

    //错误。应该将所有X全局变量放入临界区,否则是没有意义的
    DWORD WINAPI 线程A(PVOID pvParam) 		
    {		
    		
          //代码xxxxxx		
          //代码xxxxxx		
       EnterCriticalSection(&cs);		
          //对全局遍历X的操作		
       LeaveCriticalSection(&cs);		
          //代码xxxxxx		
          //代码xxxxxx		
    		
    }		
    		
    		
    DWORD WINAPI 线程B(PVOID pvParam) 		
    {		
    		
          //代码xxxxxx		
          //代码xxxxxx		
    		
          //对全局遍历X的操作		
          		
          //代码xxxxxx		
          //代码xxxxxx		
    }		
    

    4.2 应该有多少个CRITICAL_SECTION

    全局变量X
    全局变量Y
    全局变量Z
    
    线程1
    DWORD WINAPI ThreadFunc(PVOID pvParam) 
    {
       EnterCriticalSection(&g_cs);
       使用X
       使用Y
       LeaveCriticalSection(&g_cs);
       return(0);
    }
    
    线程2
    DWORD WINAPI ThreadFunc(PVOID pvParam) 
    {
       EnterCriticalSection(&g_cs);
       使用X
       使用Z
       LeaveCriticalSection(&g_cs);
       return(0);
    }
    
    线程3
    DWORD WINAPI ThreadFunc(PVOID pvParam) 
    {
       EnterCriticalSection(&g_cs);
       使用Y
       使用X
       LeaveCriticalSection(&g_cs);
       return(0);
    }
    

    解决方案:

    CRITICAL_SECTION g_csX; 
    CRITICAL_SECTION g_csY; 
    CRITICAL_SECTION g_csZ; 
    
    线程1
    DWORD WINAPI ThreadFunc(PVOID pvParam) 
    {
       EnterCriticalSection(&g_csX);
       使用X
       LeaveCriticalSection(&g_csX);
       EnterCriticalSection(&g_csY);
       使用Y
       LeaveCriticalSection(&g_csY);
    
       return(0);
    }
    
    线程2
    DWORD WINAPI ThreadFunc(PVOID pvParam) 
    {
       EnterCriticalSection(&g_csX);
       使用X
       LeaveCriticalSection(&g_csX);
       EnterCriticalSection(&g_csZ);
       使用Z
       LeaveCriticalSection(&g_csZ);
    
       return(0);
    }
    
    线程3
    DWORD WINAPI ThreadFunc(PVOID pvParam) 
    {
       EnterCriticalSection(&g_csX);
       使用X
       LeaveCriticalSection(&g_csX);
       return(0);
    }
    
    
  • 相关阅读:
    第六次作业--结对编程
    第四次作业--项目选题报告(团队)
    Eclipse和JDK的安装配置
    最大最小
    文档
    火车
    排队
    击鼓传花
    逆序数
    塔防——链表的基本操作
  • 原文地址:https://www.cnblogs.com/flatcc/p/7859074.html
Copyright © 2020-2023  润新知