4.6.1 Windows同步机制

临界区(CRITICAL_SECTION)

  • 在进程内使用,保证仅一个线程可以申请到该对象
  • 临界区内是临界资源的访问

相关的API函数

初始化临界区
WINBASEAPI
VOID
WINAPI
InitializeCriticalSection(
_Out_ LPCRITICAL_SECTION lpCriticalSection
);
删除临界区
WINBASEAPI
VOID
WINAPI
DeleteCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
退出临界区(开锁)
WINBASEAPI
VOID
WINAPI
LeaveCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);

例子

/*用3个线程共同把nSum累加到240*/
#include <cstdio>
#include <Windows.h> #define INF 0x7fffffff
//全局变量
int nSum = 0;
const int NUMBER = 80; //累加线程函数
DWORD WINAPI Accumulate(LPVOID lpParam) {
for (int i = 0; i < NUMBER; i++) {
//多个线程同时就绪,让nSum不唯一
int iCopy = nSum;
nSum = iCopy + 1;
} return 0;
} int main() {
HANDLE hThread[3];
hThread[0] = CreateThread(nullptr, 0, Accumulate, nullptr, 0, nullptr);
hThread[1] = CreateThread(nullptr, 0, Accumulate, nullptr, 0, nullptr);
hThread[2] = CreateThread(nullptr, 0, Accumulate, nullptr, 0, nullptr);
//等待这三个线程结束之后才能返回
WaitForMultipleObjects(3, hThread, true, INF); printf("nSum = %d\n", nSum); system("pause");
return EXIT_SUCCESS;
}

结果可能会出现不是240的情况

等待函数

功能:等待目标对象变成有信号的状态返回

WINBASEAPI
DWORD
WINAPI
WaitForMultipleObjects(
_In_ DWORD nCount, // 等待的目标对象的数量
_In_reads_(nCount) CONST HANDLE* lpHandles, // 目标对象的句柄
_In_ BOOL bWaitAll, // 等待方式
_In_ DWORD dwMilliseconds // 等待时间,单位是毫秒
);

等待单个对象:

WINBASEAPI
DWORD
WINAPI
WaitForSingleObject(
_In_ HANDLE hHandle, // 等待的目标对象的句柄
_In_ DWORD dwMilliseconds // 等待时间,单位是毫秒
);

修改

/*用3个线程共同把nSum累加到240*/
#include <cstdio>
#include <Windows.h> #define INF 0x7fffffff
//全局变量
int nSum = 0;
const int NUMBER = 80;
//定义临界区对象
CRITICAL_SECTION cs; //累加线程函数
DWORD WINAPI Accumulate(LPVOID lpParam) {
for (int i = 0; i < NUMBER; i++) {
//进入临界区 P(S)
EnterCriticalSection(&cs);
//多个线程同时就绪,让nSum不唯一
int iCopy = nSum;
nSum = iCopy + 1;
//退出临界区 V(S)
LeaveCriticalSection(&cs);
} return 0;
} int main() {
//初始化临界区对象
InitializeCriticalSection(&cs); HANDLE hThread[3];
hThread[0] = CreateThread(nullptr, 0, Accumulate, nullptr, 0, nullptr);
hThread[1] = CreateThread(nullptr, 0, Accumulate, nullptr, 0, nullptr);
hThread[2] = CreateThread(nullptr, 0, Accumulate, nullptr, 0, nullptr);
//等待这三个线程结束之后才能返回
WaitForMultipleObjects(3, hThread, true, INF); printf("nSum = %d\n", nSum); system("pause");
return EXIT_SUCCESS;
}

互斥量(Mutex)

  • 保证只有1个线程或进程可以申请到该对象
  • 可以跨进程使用
  • 可以有名称
  • 互斥量比临界区要耗费更多资源,速度慢

用在互斥量上的函数

//创建互斥量
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateMutexW(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner, //初始化互斥量的状态:真或假
_In_opt_ LPCWSTR lpName //名字,可为null但不能跨进程用
);
//打开一个存在的互斥量
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
OpenMutexW(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCWSTR lpName //名字
);
WINBASEAPI
BOOL
WINAPI
ReleaseMutex(
_In_ HANDLE hMutex
);
//关闭互斥量
WINBASEAPI
BOOL
WINAPI
CloseHandle(
_In_ _Post_ptr_invalid_ HANDLE hObject //句柄
);

信号量(Semaphore)

  • 允许指定数目的多个进程/进程访问临界区
  • 一种资源计数器,用于限制并发线程的数量
  • 初始值可设为N,则表示允许N个进程/线程并发访问资源

用于信号量操作的API函数

//创建信号量
WINBASEAPI
HANDLE
WINAPI
CreateSemaphoreW(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //安全属性
_In_ LONG lInitialCount, //初始值
_In_ LONG lMaximumCount, //最大值
_In_opt_ LPCWSTR lpName //名字
);
//打开信号量
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
OpenSemaphoreW(
_In_ DWORD dwDesiredAccess, //存取方式
_In_ BOOL bInheritHandle, //能否被继承
_In_ LPCWSTR lpName //名字
);
//释放信号量
WINBASEAPI
BOOL
WINAPI
ReleaseSemaphore(
_In_ HANDLE hSemaphore, //句柄
_In_ LONG lReleaseCount, //释放数,使信号量的值增加的数量
_Out_opt_ LPLONG lpPreviousCount //得到释放前信号量的值
);
//关闭信号量
WINBASEAPI
BOOL
WINAPI
CloseHandle(
_In_ _Post_ptr_invalid_ HANDLE hObject //句柄
);

信号量的值可以通过相应的函数增或减

  • WaitForSingleObject()将信号量增1
  • ReleaseSemaphore将信号量增1

信号状态

  • 信号量的值大于0时,有信号状态
  • 信号量的值小于等于0时,为无信号状态

使用信号量限制临界区访问的例子

#include <cstdio>
#include <Windows.h> #define INF 0x7fffffff //线程函数
DWORD AcessDB(void* pD) {
HANDLE hSu = OpenSemaphore(SEMAPHORE_ALL_ACCESS, (LPWSTR)L"SU");
while (true) {
WaitForSingleObject(hSu, INF);
//此处时模拟数据库访问
printf("Do database access!\n");
Sleep(100);
ReleaseSemaphore(hSu, 1, nullptr);
}
} int main() {
//创建信号灯
HANDLE hSU = nullptr;
//功能:信号量初始值为2,保证最多只有2个线程可以同时进行数据库访问
hSU = CreateSemaphore(nullptr, 2, 2, (LPWSTR)L"SU");
//创建三个线程
HANDLE hThread[3];
CWinThread* pT1 = AfxBeginThread(AccessDB, (void*)1);
CWinThread* pT2 = AfxBeginThread(AccessDB, (void*)2);
CWinThread* pT3 = AfxBeginThread(AccessDB, (void*)3);
hThread[0] = pT1->m_hThread;
hThread[1] = pT2->m_hThread;
hThread[2] = pT3->m_hThread;
WaitForMultipleObjects(3, hThread, true, INF); //关闭句柄
CloseHandle(hSU);
return EXIT_SUCCESS;
}

事件(Event)

用于通知一个或多个线程某事件出现或某操作已经完成

事件对象的分类

  • 自动重置的事件:使用WaitForSingleObject等待到事件对象变为有信号状态后该事件对象自动变为无信号状态
  • 人工重置的事件:使用WaitForSingleObject等待到事件对象变为有信号状态后该事件对象的状态不变,除非人工重置

Windows同步机制

临界区对象
EnterCriticalSection(); P操作
LeaveCriticalSection(); V操作
互斥量对象
ReleaseMutex() V操作
事件对象
Bool SetEvent() V操作
信号量对象
CreateSemaphore()
ReleaseSemaphore() V操作
等待机制
WaitForSingleObject() P操作

4.6.2 Linux同步机制

思考:程序运行流程?pid_1和pid_2的值

/*
先运行子进程,打印子进程的pid,然后返回父进程,父进程打印返回回来的子进程的pid
*/ #include <iostream>
#include <unistd.h> int main() {
pid_t pid, pid_1, pid_2;
pid = fork();
// 子进程
if (pid == 0) {
pid_1 = getpid();
printf("pid1 = %d \n", pid_1);
sleep(10);
} // 父进程
if (pid > 0) {
// 返回子进程的id号
// 子进程运行结束后,进入僵尸态,父进程来进行善后工作-
pid_2 = wait(nullptr);
printf("pid2 = %d\n", pid_2);
} exit(0);
}

进程的阻塞wait()

进程调用wait(int status)会阻塞自己

父进程调用wait函数做善后工作

  • 阻塞到有是否有子进程结束?

    • 没有:进程一直阻塞
    • 有(僵尸进程)
      • wait收集该子进程信息并彻底销毁子进程后返回
  • status保存进程退出时的状态
    • 若忽略推出信息

      • pid = wait(nullptr);

进程的终结exit()

  • 调用void exit(int status)终结进程
  • 进程终结时要释放资源并报告父进程
    • 利用status传递进程结束时的状态
    • 变为僵尸状态,保留部分PCB信息供wait收集
      • 正常结束还是异常结束
      • 占用总系统cpu事件
      • 缺页中断次数
    • 调用schedule()函数,选择新进程运行

进程的休眠

  • sleep(int nSecond)

    • 进程暂停执行nSecond秒
    • 系统暂停调度该进程
    • 相当于windows的suspend(),挂起若干秒



父子共享普通变量

#include <iostream>
#include <unistd.h> int main() {
pid_t pid;
int i = 1;
// 创建新进程
pid = fork(); // 子进程
if (pid == 0) {
i = 2;
printf("In child i = %d\n", i);
exit(0);
} else {
// 十秒休眠,让子进程先执行
sleep(10);
printf("In parent i = %d\n", i);
exit(0);
}
}

结论:对于普通变量,父子进程各自操作变量副本,互相不影响

父子进程共享文件资源

对于文件,父子进程共享同一文件和读写指针

【av68676164(p31-p32)】Windows和Linux同步机制的更多相关文章

  1. Linux同步机制(二) - 条件变量,信号量,文件锁,栅栏

    1 条件变量 条件变量是一种同步机制,允许线程挂起,直到共享数据上的某些条件得到满足. 1.1 相关函数 #include <pthread.h>  pthread_cond_t cond ...

  2. Linux同步机制(一) - 线程锁

    1 互斥锁 在线程实际运行过程中,我们经常需要多个线程保持同步. 这时可以用互斥锁来完成任务.互斥锁的使用过程中,主要有 pthread_mutex_init pthread_mutex_destor ...

  3. linux 同步机制之complete【转】

    转自: http://blog.csdn.net/wealoong/article/details/8490654 在Linux内核中,completion是一种简单的同步机制,标志"thi ...

  4. linux同步机制

    很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本 ...

  5. windows、Linux同步外网NTP服务器时间

    配置 Windows 时间服务以使用外部时间源 要将内部时间服务器配置为与外部时间源同步,请使用以下方法之一: 软件自动配置  Windows 时间服务 若要自动修复此问题,请单击“下载”按钮. 在“ ...

  6. rsync在windows和linux同步数据的配置过程

    centos7.0安装rsync3.0.9-17.el7 yum install rsync ===================================================== ...

  7. windows 和linux 同步api对比

    初始化临界区 (win) InitializeCriticalSection(RTL_CRITICAL_SECTION &rtl_critial_section) (linux) pthrea ...

  8. Linux同步机制 - 基本概念(死锁,活锁,饿死,优先级反转,护航现象)

    死锁(deadlock) 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进 ...

  9. linux同步机制2

    一.并发控制(1)自旋锁得不到资源,会原地打转,直到获得资源为止定义自旋锁 spinlock_t spin;初始化自旋锁 spin_lock_init(lock);获得自旋锁spin_lock(loc ...

随机推荐

  1. java 基本语法(七) 流程控制(四) 补充:Scanner类的使用

    /* 如何从键盘获取不同类型的变量:需要使用Scanner类 具体实现步骤: 1.导包:import java.util.Scanner; 2.Scanner的实例化:Scanner scan = n ...

  2. java 基础(三) 搭建Java编译环境(树莓派)

    安装需求1.JDK的安装2.PI4J的安装 JDK的安装1.首先到JDK的官网:https://www.oracle.com/technetwork/java/javase/downloads/ind ...

  3. 数据可视化之分析篇(九)PowerBI数据分析实践第三弹 | 趋势分析法

    https://zhuanlan.zhihu.com/p/133484654 以财务报表分析为例,介绍通用的分析方法论,整体架构如下图所示: (点击查看大图) 我会围绕这五种不同的方法论,逐步阐述他们 ...

  4. [USACO3.1]形成的区域(扫描线+离散化)

    [USACO3.1]形成的区域(P6432) 日期:2020-05-31 目录 [USACO3.1]形成的区域(P6432) 一.题意分析 二.算法分析 1. 暴力 0). 初始状态(红点为原点) 1 ...

  5. 10-Python文件操作

    一.文本文件和二进制文件 文本文件存储的是普通“字符”文本,python 默认为unicode 字符集(两个字节表示 一个字符,最多可以表示:65536 个). 二进制文件:二进制文件吧数据内容用‘字 ...

  6. 查看锁信息 v$lock 和 v$locked_object

    查看锁住的对象及会话id,serial# select a.*  from (SELECT o.object_name,               l.locked_mode,            ...

  7. ModuleNotFoundError: No module named 'phkit.pinyin'

    1 产生背景 在mac系统本地使用正常,在linux系统上phkit包缺少相应的python文件 2 解决方案 自己想出来,手动上传本地相关python代码到linux服务器 3 解决过程 首先通过项 ...

  8. consul++ansible+shell批量下发注册node_exporter

    --日期:2020年7月21日 --作者:飞翔的小胖猪  文档功能说明: 文档通过ansible+shell+consul的方式实现批量下发安装Linux操作系统监控的node_exporter软件, ...

  9. 2020想学习JAVA的同学看过来,最基础的编程CRUD你会了没?

    一 JDBC简介 Java DataBase Connectivity Java语言连接数据库 官方(Sun公司)定义的一套操作所有关系型数据库的规则(接口) 各个数据库厂商去实现这套接口 提供数据库 ...

  10. Java Web(5)-Servlet详解(上)

    一.Servlet 1. 什么是Servlet Servlet 是 JavaEE 规范之一,规范就是接口 Servlet 就 JavaWeb 三大组件之一,三大组件分别是:Servlet 程序.Fil ...