第三部分:内核区同步

等待函数(WaitForObject)

等待函数的形式

  • 单个:WaitForSingleObject

  • 多个:WaitForMultipleObjects

一个可以被等待的对象通常由两种状态,分别是:

  • 可等待(激发态)(有信号):等待函数【不会阻塞】

  • 不可等待(非激发态)(无信号):等待函数需要等待一定时长并【阻塞】

等待函数的副作用:

  • 改变被等待对象的信号状态

#include <iostream>
#include <windows.h>

// 工作线程
DWORD WINAPI WorkerThread(LPVOID lpThreadParameter)
{
// 输出 0 ~ 99
for (int i = ; i < ; ++i)
{
printf("%d\n", i);
}

return ;
}

int main()
{
HANDLE Threads[] = { };

// 创建两个线程
Threads[] = CreateThread(NULL, NULL, WorkerThread, NULL, NULL, NULL);
Threads[] = CreateThread(NULL, NULL, WorkerThread, NULL, NULL, NULL);
Threads[] = CreateThread(NULL, NULL, WorkerThread, NULL, NULL, NULL);

// 函数返回的两种情况
// 1. 被等待对象处于有信号状态
// 2. 等到超时函数会返回,但是等待失败

// 等待两个线程退出
// 1. 一个可以被等待的内核对象
// 2. 等待时长,如果是-1 表示一直等待
// WaitForSingleObject(ThreadHandles[0], INFINITE);
// WaitForSingleObject(ThreadHandles[1], INFINITE);

// 等待多个内核对象
// 1. 需要等待多少个内核对象
// 2. 内核对象句柄的数组
// 3. 是否等待所有内核对象编程变成有信号的
// 4. 等待的时长,单位是毫秒
// WaitForMultipleObjects(2, ThreadHandles, TRUE, INFINITE);

// 有任何一个执行完毕,另外一个就不执行了
WaitForMultipleObjects(, Threads, FALSE, INFINITE);

// 等待函数的副作用:修改被等待对象的信号状态

return ;
}

互斥体(Mutex)

  • 特点:拥有临界区的线程拥有者概念,但是线程崩溃不会死锁,执行较慢

  • 函数

    • 创建:CreateMutex,如果第二个参数是FALSE,那么就是可等待

    • 打开:OpenMutex

    • 保护:WaitForSingleObject: 把互斥体置为不可等待

    • 释放:ReleaseMutex把互斥体置为可等待

    • 关闭:CloseHandle

// 1. 创建一个互斥体内核对象
// - 安全属性,初始状态,名称
HANDLE Mutex = CreateMutex(NULL, FALSE, L"Mutex");

DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
{
// 为 g_Number 自增 100000 次
for (int i = ; i < ; i++)
{
// 2. 使用等待函数对互斥体进行等待,当互斥体处于激发态就会等待成功
// 当等待成功时,会将互斥体置为无信号状态
WaitForSingleObject(Mutex, INFINITE);
g_Number++;
// 3. 执行完代码之后需要恢复有信号状态
ReleaseMutex(Mutex);
}
return ;
}

事件(Event)

  • 特点:可以手动选择是否产生副作用,如果设置为手动状态,那么就不产生副作用

  • 函数:

    • 创建:CreateEvent()

    • 打开:OpenEvent()

    • 保护:WaitForSingleObject()

    • 设置为非激发态:ResetEvent()

    • 退出(设置为激发态):SetEvent()

    • 关闭:CloseHandle

// 1. 创建一个事件内核对象
// - 安全属性,是否自动,初始状态,名称
HANDLE Event = CreateEvent(NULL, FALSE, TRUE, L"Event");
// HANDLE Event = CreateEvent(NULL, TRUE, TRUE, L"Event");

DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
{
// 为 g_Number 自增 100000 次
for (int i = ; i < ; i++)
{
// 注意:如果使用手动模式,等待函数就没有副作用
// 可以使用 ResetEvent 手动设置信号状态,但是没有意义
// 原因是 ResetEvent 本身不是一个原子操作

// 2. 使用等待函数对事件进行等待,当事件处于激发态就会等待成功
// 当等待成功时,会将事件置为无信号状态
WaitForSingleObject(Event, INFINITE);
g_Number++;
// 3. 执行完代码之后需要恢复有信号状态
SetEvent(Event);
}
return ;
}

信号量

  • 特点:有多把锁,可以控制活动线程的数量,没有线程拥有者概念。

  • 如果锁的最大个数是1,则用法和互斥体类似

  • 函数:

    • 创建:CreateSemaphore

    • 打开:OpenSemaphore

    • 上锁:WaitForSingleObject

    • 解锁:ReleaseSemaphore

    • 关闭:CloseHandle

// 1. 创建一个信号量内核对象
// - 安全属性,当前信号,最大信号,名称
HANDLE Semaphore = CreateSemaphore(NULL, , , L"Semaphore");

DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
{
// 为 g_Number 自增 100000 次
for (int i = ; i < ; i++)
{
// 2. 使用等待函数对信号量进行等待,当信号个数>1表示可等待
// 当等待成功时,会将信号个数 -1
WaitForSingleObject(Semaphore, INFINITE);
g_Number++;
// 3. 执行完代码之后需要将信号个数 +1
// 信号量对象,+几个信号,之前有多少个信号
ReleaseSemaphore(Semaphore, , NULL);
}
return ;
}

Windows提高_2.3第三部分:内核区同步的更多相关文章

  1. 《windows核心编程系列》三谈谈内核对象及句柄的本质

    内核对象 本章讨论的是相对抽象的概念,不涉及任何具体的内核对象的细节而是讨论所有内核对象的共有特性. 首先让我们来了解一下什么是内核对象.内核对象通过API来创建,每个内核对象是一个数据结构,它对应一 ...

  2. Windows提高_2.1第一部分:线程

    第一部分:线程 什么是线程? 线程其实可以理解为一段正在执行中的代码,它最少由一个线程内核对象和一个栈组成. 线程之间是没有从属关系的,同一进程下的所有线程都可以访问进程内的所有内容. 主线程其实是创 ...

  3. Windows提高_2.2第二部分:用户区同步

    第二部分:用户区同步 同步和互斥 同步:就是按照一定的顺序执行不同的线程 互斥:当一个线程访问某一资源的时候,其它线程不能同时访问 多线程产生的问题 #include <stdio.h> ...

  4. Windows内核原理-同步IO与异步IO

    目录 Windows内核原理-同步IO与异步IO 背景 目的 I/O 同步I/O 异步I/O I/O完成通知 总结 参考文档 Windows内核原理-同步IO与异步IO 背景 在前段时间检查异常连接导 ...

  5. windows+mysql集群搭建-三分钟搞定集群

    注:本文来源:  陈晓婵   <  windows+mysql集群搭建-三分钟搞定集群   > 一:mysql集群搭建教程-基础篇 计算机一级考试系统要用集群,目标是把集群搭建起来,保证一 ...

  6. Windows核心编程 第三章 内核对象

    第3章内核对象 在介绍Windows API的时候,首先要讲述内核对象以及它们的句柄.本章将要介绍一些比较抽象的概念,在此并不讨论某个特定内核对象的特性,相反只是介绍适用于所有内核对象的特性. 首先介 ...

  7. windows核心编程---第三章 内核对象及句柄本质

      本章讨论的是相对抽象的概念,不涉及任何具体的内核对象的细节而是讨论所有内核对象的共有特性. 首先让我们来了解一下什么是内核对象.内核对象通过API来创建,每个内核对象是一个数据结构,它对应一块内存 ...

  8. Windows提高_1.1内核对象

    内核对象 什么是内核对象? 内核对象本质上是一个结构体,我们不能直接的操作一个内核对象,需要通过操作系统提供的一系列函数和我们使用的内核对象句柄对它进行一系列的修改. 如何操作内核对象? 创建一个内核 ...

  9. (ETW) Event Trace for Windows 提高 (含pdf下载)

    内容提纲 • 托管代码与非托管代码介绍 • 不安全代码介绍 • 用户模式与内核模式 • ETW执行流程分析 • 日志分析工具介绍:PerfView.exe   ETW与非托管代码 • ETW依赖的So ...

随机推荐

  1. Nodejs 一个简单的后台实例

    http://blog.csdn.net/u014595019/article/details/50845726

  2. C语言之基本算法21—可逆素数

    /* ================================================================== 题目:可逆素数是指一个数是素数,逆序后还是素数,如ABC是素 ...

  3. android学习一(了解android)

    声明:android学习文件中面的全部内容为都是整理来自第一行代码Android.在接下来的文章里我就不在进行反复的声明. 想看原版的能够买书看看.或者去作者的博客http://blog.csdn.n ...

  4. oracle基本建表语句

    oracle基本建表语句 2010-09-20 10:37:33|  分类: 数据库 |  标签:数据库  oracle  |字号 订阅 --创建用户create user han identifie ...

  5. Rails5 Model Document

    创建: 2017/06/09 更新: 2017/06/21 更新: 2017/06/23 对待未完成的追加# TODO: 更新: 2017/06/29 修正文件名db/seed.rb ---> ...

  6. bzoj4506: [Usaco2016 Jan]Fort Moo(暴力)

    4506: [Usaco2016 Jan]Fort Moo Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 145  Solved: 104[Submi ...

  7. php 批量检测bom头,去除bom头工具

    <?php //有些php文件由于不小心保存成了含bom头的格式而导致出现一系列的问题.以下是批量清除bom头的代码 if (isset ( $_GET ['dir'] )) { //confi ...

  8. java程序死锁,3种方式快速找到死锁代码

    java程序中出现死锁问题,如果不了解排查方法,是束手无策的,今天咱们用三种方法找到死锁问题. 运行下面代码 package com.jvm.visualvm; /** * <a href=&q ...

  9. Linux安装MySQL标准教程

    导读: 本文主要介绍 CentOS 系统二进制安装 MySQL 5.7.23 版本的安装步骤,其他版本安装过程相似. 1.前置准备 卸载旧版MySQL 查看rpm包 rpm -qa|grep mysq ...

  10. Winform执行CMD命令

    1.首先分享CmdHelper类: using System; using System.Collections.Generic; using System.Text; using System.Di ...