零基础逆向工程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\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结构的更多相关文章
- 零基础逆向工程11_C语言05_结构体
结构体小结 结构体是按照分配的大小,局部变量会自动数据对齐 1字节对齐,省空间,但cpu查找效率低 4字节对齐,不省空间,但cpu查找效率高 VC6默认的结构对齐大小 项目右键-> settin ...
- 零基础逆向工程20_PE结构04_任意节空白区_新增节_扩大节添加代码
向代码节添加代码实现 作者经过一周不断的失败,再思考以及无数次调试终于实现. 思路:八个步骤 1. 文件拷到文件缓冲区(FileBuffer) //图示见(零基础逆向工程18之PE加载过程) 2. 文 ...
- 零基础逆向工程36_Win32_10_互斥体_互斥体与临界区的区别
1 引言 讲了第二个内核对象,互斥体.前面已经学过一个内核对象,线程.这节讲两个函数,WaitForSingleObject()和WaitForMultipleObjects().因此这两个函数是根据 ...
- 零基础逆向工程34_Win32_08_线程控制_CONTEXT结构
线程控制 实验 挂起线程 ::SuspendThread(hThread); 恢复线程 ::ResumeThread(hThread); 终止线程 (这里讲了同步调用与异步调用) 方式一: 此方法结束 ...
- 零基础逆向工程22_PE结构06_导入表
导入表结构 typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstTh ...
- 零基础逆向工程23_PE结构07_重定位表_IAT表(待补充)
重定位表 待补充 IAT表 待补充
- 零基础逆向工程21_PE结构05_数据目录表_导出表
数据目录 1.我们所了解的PE分为头和节,在每个节中,都包含了我们写的一些代码和数据,但还有一些非常重要 的信息是编译器替我们加到PE文件中的,这些信息可能存在在任何可以利用的地方. 2.这些信息之所 ...
- 零基础逆向工程19_PE结构03_代码节空白区添加代码_shellcode
1.获取MessageBox地址,构造ShellCode代码 三种获取方法,参考文章末的一篇帖子. E8 E9计算公式 call 的硬编码:E8 00 00 00 00 jmp 的硬编码:E9 00 ...
- 零基础逆向工程18_PE结构02_联合体_节表_PE加载过程
联合体 特点 1.联合体的成员是共享内存空间的 2.联合体的内存空间大小是联合体成员中对内存空间大小要求最大的空间大小 3.联合体最多只有一个成员有效 节表数据结构说明 PE 加载 过程 FileBu ...
随机推荐
- sql开发技巧总结-1
1.数据库分类 关系型 非关系型 2.sql语句分类 sql: ddl数据库定义语言 tpl事物处理语言 dcl数据控制语言 dml数据操作语言(insert delete update sele ...
- luogu1447 [NOI2010]能量采集 莫比乌斯反演
link 冬令营考炸了,我这个菜鸡只好颓废数学题了 NOI2010能量采集 由题意可以写出式子: \(\sum_{i=1}^n\sum_{j=1}^m(2\gcd(i,j)-1)\) \(=2\sum ...
- 勤哲excel服务器开发外贸行业薪资考核系统
以外销为驱动的订单行业,外贸业务员的素质直接决定公司效益.然而,大多数外贸公司或以外贸为主的工厂,经常面临外贸业务人员流动性频繁的问题,易 导致客户流失及跟单的困难.科学合理的与业绩挂钩的薪资考核,可 ...
- hdu 6512 Triangle
Problem Description After Xiaoteng took a math class, he learned a lot of different shapes, but Xiao ...
- SPI 实现原理及运用
SPI原理 SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的.在java.util.ServiceLoader的文档里有比较详 ...
- Mysql tips 功能...
1. mysql GROUP_CONCAT() 使用 排序... SELECT shop.id, shop.name, shop.user_id, shop.address, shop.map_lo ...
- phpSpreadSheet 中 使用的 一些坑
如果是upupw,它 做了 安全限制...将 上传目录 写成 uploadfiles 等 才能 写进去.. 文件路径 也不要有 中文..很有可以能 下载时 找不到路径....这个太坑...
- P4245 【模板】任意模数NTT
Luogu4245 只要做三次的NTT,快的飞起 普通NTT,做9次 #include<cstdio> #include<cstring> #include<iostre ...
- 2015苏州大学ACM-ICPC集训队选拔赛(1) 1007
连通图 Time Limit : 3000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other) Total Submissio ...
- JavaSE---位运算符
1.Java支持的位运算符有7个: &:按位与 [2个相同取相同.2个不同取0] |:按位或 [2个相同取相同.2个不同取1] ~:按位非 ^:按位异或 [2个相同取0.2个不同取1] < ...