线程安全问题

每个线程都有自己的栈,而局部变量是存储在栈中的,这就意味着每个线程都有一份自己的“局部变量”,如果线程

仅仅使用 “局部变量” 那么就不存在线程安全问题

那如果多个线共用一个全局变量呢?

多线程的线程安全问题前提:

1、有全局变量

2、对全局变量有写的权限

我们写一段代码,模拟一下两个进程访问一个全局变量,代码如下:

#include <stdio.h>
#include <windows.h> int g_dwTickets = 10; DWORD WINAPI MyFirstThreadProc(LPVOID lpParameter)
{
while (g_dwTickets > 0)
{
printf("还有: %d 张票\n", g_dwTickets);
g_dwTickets--;
printf("卖出一张,还有:%d 张", g_dwTickets);
} return 0;
} //A线程
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
for (int i = 0; i < 30; i++)
{
Sleep(50);
printf("++++++++++++++ %d \n", i); } return 0;
} //B线程
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
for (int i = 0; i < 30; i++)
{
Sleep(50);
printf("++++++++++++++ %d \n", i); } return 1;
} int main()
{
HANDLE aThreadHandles[2];
DWORD dwResult1;
DWORD dwResult2; //线程A
aThreadHandles[0] = CreateThread(NULL, 0, MyFirstThreadProc, NULL, 0, NULL);
//线程B
aThreadHandles[1] = CreateThread(NULL, 0, MyFirstThreadProc, NULL, 0, NULL); //等待线程结束了
WaitForMultipleObjects(2, aThreadHandles, True, INFINITE); //当线程执行完了
GetExitCodeThread(aThreadHandles[0], &dwResult1);
GetExitCodeThread(aThreadHandles[1], &dwResult2);
printf("%d %d \n", dwResult1, dwResult2); getchar();
return 0;
}

运行一下,发现出现了写问题

多个线程对同一个全局变量访问的时候,就会存在线程安全问题

因为创建了两个线程,线程都是有独立的堆栈,各自都是不影响各自的

也就是说这段代码有两份的,各自跑各自的,但是全局变量只有一份,我写在了下图中:

因为AB两线程同时访问一个全局变量,交替的在访问一个全局变量,那么再任何一行代码执行完都有可能出现线程切换

当只剩1张票了,那么A线程判断票大于0,成立吗,很明显,这是成立的

然后再黄色区域A那边停了,就给切换到了B线程,B线程判断是否大于0

因为A线程还没有对最后一张票进行修改,所以还是1张, 1 > 0

那么B线程就执行代码,最后还有0张票,然后又切换给了 A 线程

A线程不会重头开始跑了,只会从切换前的那个地方开始跑,所以从黄色区域 A开始往下走,最后变成了 -1张票;

这样的话就会对 多线程访问同时访问全局变量的安全问题 概念更深了;

解决方法

#include <stdio.h>
#include <windows.h> int g_dwTickets = 10; DWORD WINAPI MyFirstThreadProc(LPVOID lpParameter)
{
while (g_dwTickets > 0)
{
printf("还有: %d 张票 \n", g_dwTickets);
g_dwTickets--;
printf("卖出一张,还有:%d 张\n\n", g_dwTickets);
} return 0;
} //A线程
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
for (int i = 0; i < 30; i++)
{
Sleep(50);
printf("++++++++++++++ %d \n", i); } return 0;
} //B线程
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
for (int i = 0; i < 30; i++)
{
Sleep(50);
printf("++++++++++++++ %d \n", i); } return 1;
} int main()
{
HANDLE aThreadHandles[2];
DWORD dwResult1;
DWORD dwResult2; //线程A
aThreadHandles[0] = CreateThread(NULL, 0, MyFirstThreadProc, NULL, 0, NULL);
//线程B
aThreadHandles[1] = CreateThread(NULL, 0, MyFirstThreadProc, NULL, 0, NULL); //等待线程结束了
WaitForMultipleObjects(2, aThreadHandles, TRUE, INFINITE); //当线程执行完了
GetExitCodeThread(aThreadHandles[0], &dwResult1);
GetExitCodeThread(aThreadHandles[1], &dwResult2);
printf("%d %d \n", dwResult1, dwResult2); getchar();
return 0;
}

像上面这段代码,就是没有把全局变量变为 “临界资源”,而是两个线程同时访问

访问临界资源的那段代码,叫“临界区”; 我们就需要自己构建一段临界区

我们可以自己写代码实现,也可以使用windows提供的API构建

Windows的实现方式:

再设置个全局变量,就是令牌;临界区的代码实现之前,要获取令牌,看看令牌有没有人拿,这个令牌

就是全局变量,是 1 或者 0。拿到之后设置为0,代表某人拿到了

那段代码就是对全局变量进行访问,当这个过程中其他线程会试着访问,但是会先获取令牌,令牌为0,那么就无法去访问了

临界区实现之线程锁

1、创建全局变量

CRITICAL_SECTION cs

2、初始化全局变量

InitializeCriticalSection(&cs)

3、实现临界区

EnterCriticalSection(&cs) //进入临界区

LeaveCriticalSection(&cs) //使用临界资源

开始操作,如下:

我们先创建的话这个结构体是全局的CRITICAL_SECTION,然后再初始化

然后我们就在main函数内初始化

然后我们就真正的对这个全局变量访问的时候,我们就可以构建临界区了

在读取之前,我们要构建一下临界区

两个线程用的都是同一份代码,所以可以理解成如上图那样,但是代码这样写是这样的,但是逻辑上有问题

比如在while循环判断的时候还是有问题的,这时候我们可以尝试把进入临界区的入口放到while循环前面,应该就OK了

最新文章

  1. Web后台技术趋势
  2. Yii2 事务
  3. JSBinding / Home
  4. PHP编码规范(转)
  5. Spring talk简单配置
  6. Delphi与Java中的日期互换
  7. Stm32高级定时器(四)
  8. Matlab中用内建函数代替for循环
  9. python dns查询与DNS传输漏洞查询
  10. jconsole连接本地进程报安全连接失败
  11. linux相关命令整理
  12. SpringMVC的缓存对静态资源的影响 304 Not Modified
  13. 自定义session,cookie
  14. Sublime的插件Color Highlighter的安装方法
  15. CentOS7 安装配置DNS服务器
  16. 第三百一十四节,Django框架,自定义分页
  17. URAL 1136 Parliament (DFS)
  18. ztz11的noip模拟赛T2:查房
  19. C++ Primer Plus学习:第七章
  20. NOIP 2006 提高组 t1 能量项链

热门文章

  1. linux学习(三)Linux 系统目录结构
  2. Spring的IOC控制反转和依赖注入-重点-spring核心之一
  3. keras中的mask操作
  4. Dynamically allocated memory 动态分配内存【malloc】Memory leaks 内存泄漏
  5. Arduino 跑马灯
  6. CentOS 7 系统的安装
  7. CentOS Linux 修改主机名
  8. Git操作常用的命令都在这里了。
  9. shell-字符串多操作符综合实践多案例
  10. [tip:,x86/urgent] x86: Fix early boot crash on gcc-10, third try