【av68676164(p31-p32)】Windows和Linux同步机制

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);
    }
}

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

父子进程共享文件资源

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

原文地址:https://www.cnblogs.com/iamfatotaku/p/12590302.html

时间: 2024-11-01 19:55:14

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

windows 和linux 同步api对比

初始化临界区 (win) InitializeCriticalSection(RTL_CRITICAL_SECTION &rtl_critial_section) (linux) pthread_mutexattr_init(&(mutex)->attr); pthread_mutexattr_settype(&(mutex)->attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&(mutex)->mtx

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

死锁(deadlock) 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程. 虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件. 1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用.如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放. 2)请

Linux同步机制部分术语(20%)

用于记录Linux同步机制中常见到的术语: 1. 饥饿(饿死hungry) 饥饿:指如果事务T1封锁了数据R,事务T2又请求封锁R,于是T2等待: T3也请求封锁R,当T1释放了对R的封锁之后,系统首先批准了T3的封锁请求,于是T2仍然等待: T4又请求封锁R,当T3释放了对R的封锁之后,系统又批准了T4的请求,于是T2仍然等待:... 就这样下去,T2可能永远处于等待状态,这就是饥饿(或饿死): 2. 活锁 3. 死锁 4. 优先级反转 5. 护航现象 (待整理完整...)

windows和linux套接字中的select机制浅析

先来谈谈为什么会出现select函数,也就是select是解决什么问题的? 平常使用的recv函数时阻塞的,也就是如果没有数据可读,recv就会一直阻塞在那里,这是如果有另外一个连接过来,就得一直等待,这样实时性就不是太好. 这个问题的几个解决方法:1. 使用ioctlsocket函数,将recv函数设置成非阻塞的,这样不管套接字上有没有数据都会立刻返回,可以重复调用recv函数,这种方式叫做轮询(polling),但是这样效率很是问题,因为,大多数时间实际上是无数据可读的,花费时间不断反复执行

Linux内核同步机制

http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环境来提高操作系统效率.首先,看看我们最熟悉的两种机制——信号量.锁. 一.信号量 首先还是看看内核中是怎么实现的,内核中用struct semaphore数据结构表示信号量(<linux/semphone.h>中): [cpp] view plaincopyprint? struct semaph

[内核同步]浅析Linux内核同步机制

转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral 很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本书的相关章节.趁刚看完,就把相关的内容总结一下.为了弄清楚什么事同步机制,必须要弄明白以下三个问题: 什么是互

Linux 内核的同步机制,第 1 部分 + 第二部分(转)

http://blog.csdn.net/jk198310/article/details/9264721  原文地址: Linux 内核的同步机制,第 1 部分 一. 引言 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问.尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问.在主流的Linux内核中包含了几乎所有现代的操作系统具有的同步机制,这些同步机制包括:原子操作

浅谈linux读写同步机制RCU

RCU是linux系统的一种读写同步机制,说到底他也是一种内核同步的手段,本问就RCU概率和实现机制,给出笔者的理解. [RCU概率] 我们先看下内核文档中对RCU的定义: RCU is a synchronization mechanism that was added to the Linux kernel during the 2.5 development effort that is optimized for read-mostly situations. 翻译:RCU是在2.5版本

Linux 下的同步机制

2017-03-10 回想下最初的计算机设计,在单个CPU的情况下,同一时刻只能由一个线程(在LInux下为进程)占用CPU,且2.6之前的Linux内核并不支持内核抢占,当进程在系统地址运行时,能打断当前操作的只有中断,而中断处理完成后发现之前的状态是在内核,就不触发地调度,只有在返回用户空间时,才会触发调度.所以内核中的共享资源在单个CPU的情况下其实不需要考虑同步机制,尽管表面上看起来是多个进程在同时运行,其实那只是调度器以很小的时间粒度,调度各个进程运行的结果,事实上是一个伪并行.但是随