关键代码段:

1) 关键代码段(临界区)工作在用户方式下。

2) 关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。

用InitializeCriticalSection来初始化临界区,最后用DeleteCriticalSection来释放临界区资源。在线程中用EnterCriticalSection进入关键代码段,以获得指定的临界区对象的所有权,该函数等待指定的临界区对象的所有权,如果该所有权赋予了调用线程,则函数返回,否则该函数会一直等待,从而导致线程等待。LeaveCriticalSection释放指定临界区对象的所有权。

三种实现同步方法即使用互斥对象、事件对象与关键代码段的比较

1) 互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。

2) 关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。

利用关键代码段实现线程同步(以购买车票为例):

#include "windows.h"
#include <iostream>DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);int tickets = 100;
CRITICAL_SECTION g_cs;void main()
{HANDLE hTread1;HANDLE hTread2;hTread1= CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);hTread2= CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hTread1);CloseHandle(hTread1);InitializeCriticalSection(&g_cs);Sleep(4000);DeleteCriticalSection(&g_cs);
}DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{while (true){EnterCriticalSection(&g_cs);Sleep(1);if (tickets>0){Sleep(1);std::cout<<"thread1 sell ticket: "<<tickets--<<std::endl;LeaveCriticalSection(&g_cs);}else{LeaveCriticalSection(&g_cs);break;}}return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{while (true){EnterCriticalSection(&g_cs);Sleep(1);if (tickets>0){Sleep(1);std::cout<<"thread2 sell ticket: "<<tickets--<<std::endl;LeaveCriticalSection(&g_cs);}else{LeaveCriticalSection(&g_cs);break;}}return 0;
}

因为多个线程需要访问临界区对象,因此将其定义为全局变量,即上述代码定义的CRITICAL_SECTION 类型的对象:g_cs

首先在main中调用InitializeCriticalSection函数创建临界区对象,并在程序退出前,需要调用DeleteCriticalSection函数释放没有被任何线程使用的临界区对象那个的所有资源。

在主线程中创建的连个线程中,在进入关键代码段访问受保护的代码之前,需要调用EnterCriticalSection函数,以判断是否能得到指定的临界区对象的所有权,如果无法得到该多有权,那么EnterCriticalSection函数会一直等待,从而导致线程暂停运行;如果能够得到该所有权,那么该线程就进入到关键代码段,访问受保护的资源。

注意:在得到临界区对象的所有权之后,一定要释放该所有权。

但由于很容易因为粗心大意引起关键代码段造成死锁问题,因此使用时要特别小心。哲学家进餐问题是典型的线程死锁问题,其描述为:多位哲学家一起用餐,但每人只用一个筷子,当然一根筷子是无法吃到食物的,这是如果有一位哲学家愿意交出他自己的筷子,让其他人先吃,之后再将一双筷子交回来,这样所有的哲学家就都能吃到食物了。但是哲学家考虑的比较多,他们担心把筷子交给别人先吃,那么别人吃完食物之后不把筷子交回来的话,自己就吃不到食物了,所以他们都希望其他人先把筷子交出来,让自己先吃。然而由于每位哲学家都这样想,从而导致每位这削减都不肯交出筷子,于是所有的哲学家看着满桌子的美食,可就是吃不到,这就是一个线程死锁问题的实例。

对多线程来说,如果线程1拥有了临界区对象A,等待临界区对象B的拥有权;线程2拥有了临界区对象B,等待临界区对象A的拥有权,这就造成了死锁,下面通过程序演示死锁的发生:

#include "windows.h"
#include <iostream>DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);int tickets = 100;
CRITICAL_SECTION g_csA;
CRITICAL_SECTION g_csB;void main()
{HANDLE hTread1;HANDLE hTread2;hTread1= CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);hTread2= CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hTread1);CloseHandle(hTread1);InitializeCriticalSection(&g_csA);InitializeCriticalSection(&g_csB);Sleep(4000);DeleteCriticalSection(&g_csA);DeleteCriticalSection(&g_csB);
}DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{while (true){EnterCriticalSection(&g_csA);Sleep(1);EnterCriticalSection(&g_csB);if (tickets>0){Sleep(1);std::cout<<"thread1 sell ticket: "<<tickets--<<std::endl;LeaveCriticalSection(&g_csB);LeaveCriticalSection(&g_csA);}else{LeaveCriticalSection(&g_csB);LeaveCriticalSection(&g_csA);break;}}return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{while (true){EnterCriticalSection(&g_csB);Sleep(1);EnterCriticalSection(&g_csA);if (tickets>0){Sleep(1);std::cout<<"thread2 sell ticket: "<<tickets--<<std::endl;LeaveCriticalSection(&g_csA);LeaveCriticalSection(&g_csB);}else{LeaveCriticalSection(&g_csA);LeaveCriticalSection(&g_csB);break;}}return 0;
}

首先创建了两个临界区对象:g_csA, g_csB。在main()中,调用InitializeCriticalSection函数对两个对象进行了初始化,并在程序退出之前调用了DeleteCriticalSection释放临界区对象的所有资源;然后再线程1中调用EnterCriticalSection函数请求临界区对象:g_csA的所有权,当得到该所有权后,再去请求临界区对象g_csB的所有权,线程1访问完保护的资源后,调用LeaveCriticalSection函数释放两个临界区对象的所有权。 注意:因为调用LeaveCriticalSection时,该函数会立即返回,并不会导致线程等待,所以释放临界区对象所有权的顺序是无所谓的。

对线程2来说,它先请求临界区对象g_csB的所有权,然后再去等待临界区对象g_csA的所有权。在访问完保护资源后,释放所有临界区对象的所有权。

下面我们分析如何造成了死锁:

当线程1 得到临界区对象g_csA的所有权之后,调用Sleep()函数,该线程将休眠1毫秒,放弃执行机会。于是,操作系统会选择线程2来执行,该线程首先等待的是临界区对象g_csB的所有权,当它得到该所有权之后,调用Sleep()函数,让线程2也休眠1毫秒。于是轮到线程1执行,这是它需要等待临界区对象g_csB的所有权。然而这时临界区 对象g_csB已经被线程2所拥有,因此线程1就会等待,当线程1等待时,线程2就会执行,这时它需要等待临界区g_csA的所有权,然而临界区对象g_csA的所有权已经被线程1所拥有,因此线程2也进入等待状态。从而导致线程1和线程2都在等待对方交出临界区对象的所有权,于是就造成了死锁。

线程死锁结果

线程同步之关键代码段相关推荐

  1. 线程同步--关键代码段(三)

    前面讲述了如何使用关键代码段. 但是关键代码段还有一些不为人知的秘密,也是多数程序员忽略的东西,特别是在多核cpu上面,我们必须知道的东西. 下面是<windows核心编程>这本书里面的一 ...

  2. Windows编程-- 用户方式中线程的同步---关键代码段(临界区)

    可以从例子学习,更好的掌握 #include <windows.h> #include <iostream.h> //两个线程的声明 DWORD WINAPI Fun1Proc ...

  3. 线程同步--关键代码段(一)

    线程同步有四种方式 但是在一个进程中,效率最高的,方式是 :关键代码段 #include <iostream> #include <windows.h> #include &l ...

  4. VC++中多线程学习(MFC多线程)三(线程同步包含:原子互锁、关键代码段、互斥器Mutex、Semaphores(信号量)、Event Objects(事件))

    目录 ​​​​​​​​​​​​ 线程同步的必要性: 2.解决同步问题的方法 2.1原子互锁家族函数 2.2Critical Sections(关键代码段.关键区域.临界区域) 2.3 互斥器Mutex ...

  5. 【Window】线程同步方式1——临界区(关键代码段)

    第一节:[Window]创建线程的3种方式 第二节:[Window]线程同步概述 第三节:[Window]线程同步方式1--临界区(关键代码段) 第四节:[Window]线程同步方式2--互斥量 第五 ...

  6. 互斥对象与关键代码段的比较

    9.6.2 互斥对象与关键代码段的比较 就等待线程的调度而言,互斥对象与关键代码段之间有着相同的特性.但是它们在其他属 性方面却各不相同.表 9 - 1 对它们进行了各方面的比较. 表 9-1 互斥对 ...

  7. 线程同步--关键代码段(二)

    在我们接触到的多线程书籍里面,提到最多的就是线程同步问题了. 但是,我们看到最多的例子也是对一个临界资源的访问. 但是当我们自认为感觉靴子很好的时候,问题出现了,怎么才能够使我们让线程按照一定的顺序访 ...

  8. 秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量

    前面<秒杀多线程第四篇一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,这个问题包括了主线程与子线程的同步,子线程间的互斥,是一道非常经典的多线程同步互斥问题范例,后面分别用了 ...

  9. 经典线程同步总结 关键段 事件 互斥量 信号量

    本文参考了http://blog.csdn.net/morewindows/article/details/7538247 1.线程(进程)同步的主要任务 答:在引入多线程后,由于线程执行的异步性,会 ...

最新文章

  1. 关于Python3.9,看这张16岁高中生做的「新特性必知图」就够了
  2. java 线程组和线程_Java多线程 线程组原理及实例详解
  3. mysql数据库表空间最大值_mysql 数据库取最大值
  4. 52.Linux/Unix 系统编程手册(下) -- POSIX 消息队列
  5. UG软件_NX1926中文版网盘下载链接+安装教程
  6. 网络管理软件免费linux,SugarNMSTool免费版
  7. 物联网——无线通信技术
  8. BIG5, GB(GB2312, GBK, ...), Unicode编码, UTF8, WideChar, MultiByte, Char说明与区别
  9. 一篇教你随意下载网易云音乐歌曲的博客!
  10. 移植MotionDriver到RTT
  11. [转] 大学的终结—1950年代初期的“院系调整”
  12. 计算机导论——计算机软件03
  13. 老无所依nbsp;(聊后版)
  14. Linux dns劫持程序,linux的dns被劫持(解决方案)
  15. LSVGlobal Mapper应用----地形下载
  16. Whitelabel Error Page 的原因分析
  17. 电商运营基本常识你都知道哪些?
  18. 2021 年软件开发趋势大预测!
  19. 【汇正财经】什么是股票交割方式?股票交割方式有哪些?
  20. Python之禅——传说中的蛇宗总纲

热门文章

  1. python之time模块和hashlib模块
  2. BZOJ3110: [Zjoi2013]K大数查询
  3. CSS基础必备盒模型及清除浮动
  4. Sersync实现触发式文件同步
  5. 苹果也像谷歌一样,玩起了自己的X
  6. php or || 和 and
  7. 阅读第13,14,15,16,17章
  8. IOS - JSON数据解析 小3种方法
  9. 数据挖掘 —— 模型评估
  10. Kaggle债务违约预测冠军经验分享