Windows多线程——临界区、事件、互斥量、信号量详解加代码

简介: Windows多线程——临界区、事件、互斥量、信号量详解加代码

一、【临界区】


每个进程中访问临界资源的那段代码称为临界区(Critical Section)(临界 资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区, 进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程 必须互斥地对它进行访问。


多个进程中涉及到同一个临界资源的临界区称为相关临界区。


【进程进入临界区的调度原则】


如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。


任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区, 则其它所有试图进入临界区的进程必须等待。


进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。


如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。


代码实现如下:

#include<Windows.h>
#include<iostream>
using namespace std;
CRITICAL_SECTION  g_cs;   // 临界区全局变量
char g_Str[100];
DWORD WINAPI ThreadFunc1(PVOID pParam)
{
  EnterCriticalSection(&g_cs);  //进入临界区
  for (int i = 0; i < 100; i++)
  {
    g_Str[i] = 'Y';     //为数组赋值
    Sleep(10);
  }
  LeaveCriticalSection(&g_cs);
  return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pParam)
{
  EnterCriticalSection(&g_cs);  //进入临界区
  for (int i = 0; i < 100; i++)
  {
    g_Str[100 - i - 1] = 'I'; //为数组赋值
    Sleep(10);
  }
  LeaveCriticalSection(&g_cs);
  return 0;
}
int main()
{
  //初始化临界区
  InitializeCriticalSection(&g_cs);
  HANDLE hTH1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);
  HANDLE hTH2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);
  HANDLE TH[2] = { hTH1, hTH2 };
  //等待所有内核对象,TRUE等待所有信号量有效再往下执行,FALSE当有其中一个信号量有效时有向下运行
  WaitForMultipleObjects(2, TH, FALSE, INFINITE);
  //删除临界区
  DeleteCriticalSection(&g_cs);
  printf(g_Str);
  getchar();
  return 0;
}


二、【事件】


事件的主要用途是标志事件的发生,并以此协调线程的执行顺序。


用来通知线程有一些事件已发生,从而启动后继续任务的开始。


事件对象也可以通过通知操作方式来保持线程的同步,并且可以实现不同进程中的线程同步操作。


实现代码如下:

//线程间通信--》事件  Event
#include<Windows.h>
#include<iostream>
using namespace std;
HANDLE evRead, evFinish;
DWORD WINAPI ReadThread(PVOID pParam)
{
  // 当等待仍在挂起状态的时候,句柄被关闭
  WaitForSingleObject(evRead, INFINITE);
  cout << "Reading Event: " << endl;
  return 0;
}
DWORD WINAPI WriteThread(PVOID pParam)
{
  cout << "Writing Event: " << endl;
  SetEvent(evRead);
  return 0;
}
int main()
{
  evRead = CreateEvent(NULL, FALSE, FALSE, NULL);
  evFinish = CreateEvent(NULL, FALSE, FALSE, NULL);
  CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);
  Sleep(1000);
  CreateThread(NULL, 0, WriteThread, NULL, 0, NULL);
  WaitForSingleObject(evFinish, INFINITE);  //等待信号量
  cout << "The Program is End,OK\n\n";
  return 0;
}


三、【互斥量】


互斥量又称互斥锁。互斥量是一个可以处于两态之一的变量:解锁和加锁。


如果不需要信号量的计数能力,有时可以使用信号量的一个简化版本,称为互斥量

(mutex)。


互斥量仅仅适用于管理共享资源或一小段代码。


由于互斥量在实现时既容易又有效,这使得互斥量在实现用户空间线程包时非常有 用。


为协调共同对一个共享资源的单独访问而设计的。


实现代码如下:

#include<Windows.h>
#include<iostream>
#include<fstream>
#include<atltime.h>   //CTIME  时间类头文件
using namespace std;
fstream fstrobj;
DWORD WINAPI ThreadFunc1(PVOID pParam)
{
  HANDLE *phMutex = (HANDLE*)pParam;
  for (int i = 1; i <= 1000000; i++)
  {
    WaitForSingleObject(*phMutex, INFINITE);
    fstrobj << "ThreadFunc1:" << i << endl;
    ReleaseMutex(*phMutex);
  }
  return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pParam)
{
  HANDLE *phMutex = (HANDLE*)pParam;
  for (int i = 1; i <= 1000000; i++)
  {
    WaitForSingleObject(*phMutex, INFINITE);
    fstrobj << "ThreadFunc2:" << i << endl;
    ReleaseMutex(*phMutex);
  }
  return 0;
}
int main()
{
  //获取初始当前时间
  CTime timebg = CTime::GetCurrentTime();
  int nHour = timebg.GetHour();
  int nMin = timebg.GetMinute();
  int nSec = timebg.GetSecond();
  fstrobj.open("MutexDataFile.txt", ios::out);
  if (!fstrobj.is_open())
  {
    cout << "文件打开失败!" << endl;
  }
  HANDLE hMutex = CreateMutex(NULL, FALSE, L"PrintMutexDataInfo IS:");
  HANDLE TH1 = CreateThread(NULL, 0, ThreadFunc1, &hMutex, 0, NULL);
  HANDLE TH2 = CreateThread(NULL, 0, ThreadFunc2, &hMutex, 0, NULL);
  HANDLE hTH[2] = { TH1, TH2 };
  WaitForMultipleObjects(2, hTH, TRUE, INFINITE);  // 等待互斥量对象
  CloseHandle(hMutex);
  fstrobj.close();
  //获取结束当前时间
  CTime timeed = CTime::GetCurrentTime();
  int nHour2 = timeed.GetHour();
  int nMin2 = timeed.GetMinute();
  int nSec2 = timeed.GetSecond();
  //时间差变量
  CTimeSpan timeSpan;
  timeSpan = timeed - timebg;
  // 统计出执行200万次需要多长时间
  cout << "共用时:" << timeSpan.GetHours() << "小时" << timeSpan.GetMinutes()
    << "分钟" << timeSpan.GetSeconds() << "秒" << endl;
  return 0;
}


四、【信号量(Semaphores)】


(主要是实现同步,可以跨进程) 信号量是一个内核对象,可用来管理大量有限的系统资源


一个使用计数


32位整数,最大资源数量


32位整数,当前资源数量


信号量使用规则:


当前资源数量大于0,则等待信号量的线程获得资源继续运行,当前资源数量 减1


当前资源数量等于0,则等待信号量的线程继续等待,直到有线程释放信号量, 使当前资源数量大于0


创建信号量

HANDLE CreateSemaphore(  PSECURITY_ATTRIBUTES psa, 
  LONG lInitialCount, // initial count 
  LONG lMaximumCount,  PCTSTR pszName                    
);


为现有的一个已命名信号机对象创建一个新句柄。


HANDLE OpenSemaphore(
DWORD fdwAccess, 
BOOL bInheritHandle, // inheritance option  
PCTSTR pszName // object name ); 


SEMAPHORE_ALL_ACCESS 要求对信号量的完全访问;


SEMAPHORE_MODIFY_STATE 允许使用ReleaseSemaphore函数;


SYNCHRONIZE 允许使用信号量同步。


释放信号量

ReleaseSemaphore(
    HANDLE hSem,
    LONG lReleaseCount,
    PLONG plPreviousCount
   );


等待互斥量


DWORD WaitForSingleObject( 
HANDLE hHandle, 
DWORD dwMilliseconds 
);


实现代码如下:


#include <Windows.h>
#include <stdio.h>
DWORD WINAPI ThreadFunc1(LPVOID lParam);
DWORD WINAPI ThreadFunc2(LPVOID lParam);
DWORD WINAPI ThreadFunc3(LPVOID lParam);
DWORD WINAPI ThreadFunc4(LPVOID lParam);
DWORD WINAPI ThreadFunc5(LPVOID lParam);
DWORD WINAPI ThreadFunc6(LPVOID lParam);
DWORD WINAPI ThreadFunc7(LPVOID lParam);
HANDLE hSM1;
HANDLE hSM2;
HANDLE hSM3;
HANDLE hSM4;
HANDLE hSM5;
HANDLE hSM6;
HANDLE hSM7;
HANDLE hTH1;
HANDLE hTH2;
HANDLE hTH3;
HANDLE hTH4;
HANDLE hTH5;
HANDLE hTH6;
HANDLE hTH7;
int main()
{
  // 创建6个信号量
  hSM1 = CreateSemaphore(NULL, 1, 1, "V");
  hSM2 = CreateSemaphore(NULL, 0, 1, "i");
  hSM3 = CreateSemaphore(NULL, 0, 1, "c");
  hSM4 = CreateSemaphore(NULL, 0, 1, "o");
  hSM5 = CreateSemaphore(NULL, 0, 1, "N");
  hSM6 = CreateSemaphore(NULL, 0, 1, "B");
  hSM7 = CreateSemaphore(NULL, 0, 1, " ");
  // 创建6个线程
  hTH1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);
  hTH2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);
  hTH3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);
  hTH4 = CreateThread(NULL, 0, ThreadFunc4, NULL, 0, NULL);
  hTH5 = CreateThread(NULL, 0, ThreadFunc5, NULL, 0, NULL);
  hTH6 = CreateThread(NULL, 0, ThreadFunc6, NULL, 0, NULL);
  hTH7 = CreateThread(NULL, 0, ThreadFunc7, NULL, 0, NULL);
  // 等待6个线程都执行完毕后
  WaitForSingleObject(hTH1, INFINITE);
  WaitForSingleObject(hTH2, INFINITE);
  WaitForSingleObject(hTH3, INFINITE);
  WaitForSingleObject(hTH4, INFINITE);
  WaitForSingleObject(hTH5, INFINITE);
  WaitForSingleObject(hTH6, INFINITE);
  WaitForSingleObject(hTH7, INFINITE);
  // 关闭句柄 
  CloseHandle(hTH1);
  CloseHandle(hTH2);
  CloseHandle(hTH3);
  CloseHandle(hTH4);
  CloseHandle(hTH5);
  CloseHandle(hTH6);
  CloseHandle(hTH7);
  // 关闭信号量
  CloseHandle(hSM1);
  CloseHandle(hSM2);
  CloseHandle(hSM3);
  CloseHandle(hSM4);
  CloseHandle(hSM5);
  CloseHandle(hSM6);
  CloseHandle(hSM7);
  printf("\n\n\n");
  return 0;
}
DWORD WINAPI ThreadFunc1(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM1, INFINITE);
    printf("V");
    ReleaseSemaphore(hSM2, 1, NULL);
  }
  return 0;
}
DWORD WINAPI ThreadFunc2(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM2, INFINITE);
    printf("i");
    ReleaseSemaphore(hSM3, 1, NULL);
  }
  return 0;
}
DWORD WINAPI ThreadFunc3(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM3, INFINITE);
    printf("c");
    ReleaseSemaphore(hSM4, 1, NULL);
  }
  return 0;
}
DWORD WINAPI ThreadFunc4(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM4, INFINITE);
    printf("o");
    ReleaseSemaphore(hSM5, 1, NULL);
  }
  return 0;
}
DWORD WINAPI ThreadFunc5(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM5, INFINITE);
    printf("N");
    ReleaseSemaphore(hSM6, 1, NULL);
  }
  return 0;
}
DWORD WINAPI ThreadFunc6(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM6, INFINITE);
    printf("B");
    ReleaseSemaphore(hSM7, 1, NULL);
  }
  return 0;
}
DWORD WINAPI ThreadFunc7(LPVOID lParam)
{
  for (int i = 0; i < 10; i++)
  {
    DWORD dwWait = WaitForSingleObject(hSM7, INFINITE);
    printf(" ");
    ReleaseSemaphore(hSM1, 1, NULL);
  }
  return 0;
}


小结


临界区:主要通过对多线程串行化来访问公共资源或一段代码,速度快,适合控制数据访问场合;


互斥量:为协议共同对一个共享资源数据的单独访问而设计的;


信号量:为控制一个具有有限数量用户资源而设计;


事件:用来通知线程有一些事件即将发生,从而启动后继任何的开始。

相关文章
|
8月前
|
安全 生物认证 网络安全
windows10无法设置默认保存位置怎么办?显示错误代码0x80070002怎么解决?
Win10系统下载文件时,默认会保存在特定位置,但用户可自行修改。若更改后仍无效,可通过删除目标磁盘中的特定文件夹、修改注册表权限、“干净启动”排除干扰软件或使用第三方修复工具等方式解决此问题。
1473 0
|
存储 安全 UED
Cyber Triage 3.13 for Windows - 数字取证和事件响应
Cyber Triage 3.13 for Windows - 数字取证和事件响应
353 71
Cyber Triage 3.13 for Windows - 数字取证和事件响应
|
7月前
|
Windows
office出现0xc0000142错误?windows错误代码为0xc0000142?
office出现0xc0000142错误?windows错误代码为0xc0000142?
340 0
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
700 3
|
缓存 安全 网络协议
使用事件日志识别常见 Windows 错误
事件查看器是Windows操作系统中的标准诊断工具,用于记录系统事件,包括硬件问题、软件中断和系统行为等详细信息。通过分析这些日志,管理员能够追踪和解决系统错误。访问方法包括使用快捷键Win + R输入eventvwr.msc,或通过控制面板进入。事件查看器中的每条记录包含事件ID、来源和描述,帮助识别和解决问题。常见错误如蓝屏死机、DLL错误、驱动程序错误等,可通过更新驱动程序、运行系统诊断、使用恢复功能等方式解决。
1319 4
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
244 6
|
Linux 开发者 Python
从Windows到Linux,Python系统调用如何让代码飞翔🚀
【9月更文挑战第10天】在编程领域,跨越不同操作系统的障碍是常见挑战。Python凭借其“编写一次,到处运行”的理念,显著简化了这一过程。通过os、subprocess、shutil等标准库模块,Python提供了统一的接口,自动处理底层差异,使代码在Windows和Linux上无缝运行。例如,`open`函数在不同系统中以相同方式操作文件,而`subprocess`模块则能一致地执行系统命令。此外,第三方库如psutil进一步增强了跨平台能力,使开发者能够轻松编写高效且易维护的代码。借助Python的强大系统调用功能,跨平台编程变得简单高效。
412 1
Windows7电脑启动时提示文件winload.exe无法验证其数字签名,错误代码0xc0000428的解决方法
Windows7电脑启动时提示文件winload.exe无法验证其数字签名,错误代码0xc0000428的解决方法
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
961 0
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
1655 0

热门文章

最新文章