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\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;
}

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

零基础逆向工程35_Win32_09_临界区_CRITICAL_SECTION结构的更多相关文章

  1. 零基础逆向工程11_C语言05_结构体

    结构体小结 结构体是按照分配的大小,局部变量会自动数据对齐 1字节对齐,省空间,但cpu查找效率低 4字节对齐,不省空间,但cpu查找效率高 VC6默认的结构对齐大小 项目右键-> settin ...

  2. 零基础逆向工程20_PE结构04_任意节空白区_新增节_扩大节添加代码

    向代码节添加代码实现 作者经过一周不断的失败,再思考以及无数次调试终于实现. 思路:八个步骤 1. 文件拷到文件缓冲区(FileBuffer) //图示见(零基础逆向工程18之PE加载过程) 2. 文 ...

  3. 零基础逆向工程36_Win32_10_互斥体_互斥体与临界区的区别

    1 引言 讲了第二个内核对象,互斥体.前面已经学过一个内核对象,线程.这节讲两个函数,WaitForSingleObject()和WaitForMultipleObjects().因此这两个函数是根据 ...

  4. 零基础逆向工程34_Win32_08_线程控制_CONTEXT结构

    线程控制 实验 挂起线程 ::SuspendThread(hThread); 恢复线程 ::ResumeThread(hThread); 终止线程 (这里讲了同步调用与异步调用) 方式一: 此方法结束 ...

  5. 零基础逆向工程22_PE结构06_导入表

    导入表结构 typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstTh ...

  6. 零基础逆向工程23_PE结构07_重定位表_IAT表(待补充)

    重定位表 待补充 IAT表 待补充

  7. 零基础逆向工程21_PE结构05_数据目录表_导出表

    数据目录 1.我们所了解的PE分为头和节,在每个节中,都包含了我们写的一些代码和数据,但还有一些非常重要 的信息是编译器替我们加到PE文件中的,这些信息可能存在在任何可以利用的地方. 2.这些信息之所 ...

  8. 零基础逆向工程19_PE结构03_代码节空白区添加代码_shellcode

    1.获取MessageBox地址,构造ShellCode代码 三种获取方法,参考文章末的一篇帖子. E8 E9计算公式 call 的硬编码:E8 00 00 00 00 jmp 的硬编码:E9 00 ...

  9. 零基础逆向工程18_PE结构02_联合体_节表_PE加载过程

    联合体 特点 1.联合体的成员是共享内存空间的 2.联合体的内存空间大小是联合体成员中对内存空间大小要求最大的空间大小 3.联合体最多只有一个成员有效 节表数据结构说明 PE 加载 过程 FileBu ...

随机推荐

  1. GuavaCache简介(一)

    原文地址 http://blog.csdn.net/guozebo/article/details/51590517 前言 在多线程高并发场景中往往是离不开cache的,需要根据不同的应用场景来需要选 ...

  2. HTTP基本认证和JWT鉴权

    一.HTTP基本认证 Basic Authentication——当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: · 客户端发送http请求 ·  ...

  3. 关于NSFetchedResultsController的一些用法

    如何在iPhone等设备持久保存数据,需要用到Core Data,它能帮你快速而有效率的完成数据储存,Core Data 编程经常用到 NSFetchedResultsController这个类,刚开 ...

  4. [HAOI2006]受欢迎的牛 tarjan缩点 BZOJ1051

    题目背景 本题测试数据已修复. 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就是一头明星奶牛.所有奶 牛都是自恋狂,每头奶牛总是喜欢自己的.奶牛之间的“喜欢”是可以传递的——如果A喜 ...

  5. mongoDB副本集+分片集群

    首先搭建一个副本集(三台机器) 主,从,仲裁 然后搭建分片shard1,在每台机子上启用shard1(这里就写一个分片吧!!如果写多了怕初学者会混乱,先写一个.然后可以按照同样的方法写第二个,第三个) ...

  6. Mybatis学习笔记(一) —— mybatis介绍

    一.Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名 ...

  7. 江西财经大学第一届程序设计竞赛 D

    链接:https://www.nowcoder.com/acm/contest/115/D来源:牛客网 题目描述 事情,是这样的. 有这么一天双休日的中午. 我刚把我衣服扔进了洗衣机,然后拿了个小板凳 ...

  8. php中静态绑定

    自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类. 虽然也可以调用非静态方法,但是不会在运行时绑定. static 不再只是简单的静态修饰关键字. ...

  9. SQL Connect By 的例子

    看到一个较为通俗易懂的connect by的例子,是百度知道的答案,稍微整理了一下.我自己这样理解:connect by prior "id" = "p_id" ...

  10. Ansible自动化运维工具

    ansible软件介绍 python语言是运维人员必会的语言!  ansible是一个基于Python开发的自动化运维工具!(saltstack)  其功能实现基于SSH远程连接服务!  ans ...