多线程基础之五:Windows API提供的mutex和semaphore性能比较

news/2024/5/18 7:14:49 标签: windows, 多线程, mutex, semaphore, 性能

Windows系统提供HANDLE WINAPI CreateSemaphore(xxx)HANDLE WINAPI CreateMutex(xxx)直接提供Mutex和Semaphore两种内核对象供程序员使用在临界区互斥操作。在前面的多线程基础之一提到过Semaphore存在Binary和Counting两种,其中Binary Semaphore等同于Mutex,而Counting Semaphores则存在资源计数的选项,并且还存在可以为Counting Semaphores配备等待队列实现非阻塞请求以实现加速操作。

但是我们仔细查看创建Semaphore的Windows API:

HANDLE WINAPI CreateSemaphore( 
  _In_opt_  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes 
  _In_      LONG lInitialCount, 
  _In_      LONG lMaximumCount, 
  _In_opt_  LPCTSTR lpName 
);
第一个参数:安全属性,如果为NULL则是默认安全属性 
第二个参数:信号量的初始值,要>=0<=第三个参数 
第三个参数:信号量的最大值,即最大资源数目 
第四个参数:信号量的名称,一般不涉及到跨进程使用基本都是输入NULL的。 

可以发现并不存在开关配备等待队列的选项,是否这意味着Windows系统提供的Semaphore并没有提供这一选项还是内嵌了等待队列?这需要代码检验一下。

test_time_Mutex.cpp

#include <iostream>
#include <Windows.h>
#include <time.h>
using namespace std;

HANDLE  g_hMutex = NULL;
const int g_Number = 50;
const int killTimeStep = 100000000;
DWORD WINAPI ThreadProc(__in  LPVOID lpParameter);

int main()
{
    clock_t start_time = clock();

    //TRUE代表主线程拥有互斥对象 但是主线程没有释放该对象  互斥对象谁拥有 谁释放 
    g_hMutex = CreateMutex(NULL,TRUE,NULL);
    printf("主线程创建时便占有了互斥对象,没有释放,所以其他子线程无法使用。\n");

    HANDLE hThread[ g_Number ] = {0}; //创建50个线程

    int first = 1;
    for (int i=0; i<g_Number; i++)
    hThread[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)first++, 0, NULL);

    printf("创建大规模子线程成功\n");

    ReleaseMutex(g_hMutex);
    printf("主线程释放了互斥对象,其他子线程可以开始使用。\n");

    WaitForMultipleObjects(g_Number,hThread,TRUE,INFINITE);

    for (int i=0; i<g_Number; i++)
    CloseHandle( hThread[i] );

    CloseHandle( g_hMutex );
    clock_t end_time = clock();
    cout<<"Running time is:"<<static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl;
    return 0;
}

DWORD WINAPI ThreadProc(__in  LPVOID lpParameter)
{
    WaitForSingleObject(g_hMutex, INFINITE);//等待互斥量
    cout<<(int)lpParameter<<endl;
    int i = killTimeStep;
    while(i--);
    ReleaseMutex(g_hMutex);//释放互斥量
    return 0;
}

运行结果
这里写图片描述
********************************……********************************
这里写图片描述
test_time_Semaphores.cpp

#include <iostream>
#include <Windows.h>
#include <time.h>
using namespace std;

HANDLE  g_Sema = NULL;
const int g_Number = 50;
const int killTimeStep = 100000000;
DWORD WINAPI ThreadProc(__in  LPVOID lpParameter);

int main()
{
    clock_t start_time = clock();

    g_Sema = CreateSemaphore(NULL,0,3,NULL); 
    printf("主线程创建时便占有了互斥对象,没有释放,所以其他子线程无法使用。\n");
    ReleaseSemaphore(g_Sema,1,NULL);
    printf("主线程释放了互斥对象,其他子线程可以开始使用。\n");

    HANDLE hThread[ g_Number ] = {0}; //创建50个线程

    int first = 1;
    for (int i=0; i<g_Number; i++)
    hThread[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)first++, 0, NULL);

    WaitForMultipleObjects(g_Number,hThread,TRUE,INFINITE);

    for (int i=0; i<g_Number; i++)
    CloseHandle( hThread[i] );

    CloseHandle( g_Sema );
    clock_t end_time = clock();
    cout<<"Running time is:"<<static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl;
    return 0;
}

DWORD WINAPI ThreadProc(__in  LPVOID lpParameter)
{
    WaitForSingleObject(g_Sema, INFINITE);//等待信号量 
    cout<<(int)lpParameter<<endl; 
    int i = killTimeStep;
    while(i--);
    ReleaseSemaphore(g_Sema,1,NULL);//释放信号量 
    return 0; 
}

运行结果
这里写图片描述
********************************……********************************
这里写图片描述

Conclusion: 可以看到Windows确实没有为Semaphore配备等待队列,所以导致在测试代码中Semaphore和Mutex在同样的条件下都是采用“阻塞”请求机制,即在线程获取CPU的轮转时间片时,会一直调用WaitForSingleObject(xxx)询问情况,直到耗光本轮时间片或得到互斥对象。

对于操作损耗较短的临界区,性能影响并不明显(进程上下文切换的花费甚至可能还抵不过进程非阻塞请求节省下来的时间)。但是如果临界区的操作消耗时间较长,显然为Semaphore配备等待队列机制是存在需求的。参见多线程基础之六:采用Pthread Win32提供的配备等待队列的Semaphore填补Windows未提供非阻塞请求的Semaphore机制。


http://www.niftyadmin.cn/n/1030872.html

相关文章

多线程基础之六:Pthread Win32实现的非阻塞请求机制的Semaphore

前面看到Windows API直接提供的Semaphore并没有为其配备等待队列&#xff0c;从而无法实现非阻塞请求机制以实现操作加速&#xff0c;对于临界区耗时的情况下&#xff0c;显然是存在实现非阻塞请求机制的Semaphore的。Linux下的Pthread库实现了这样的增加版Semaphore&#xff0…

UWA DAY 2020 课程视频已上线

“品往鉴来&#xff0c;质存高远” UWA DAY 2020已落下帷幕&#xff0c;UWA再次以丰富多样的专题内容、具有前瞻性的技术洞见呈现于众。再次感谢您对本次大会的关注与支持&#xff0c;希望对您而言这是一段美好的回忆。UWA DAY 2020的大部分议题已发布于UWA学堂&#xff0c;欢迎…

多线程基础之七:多线程遇上printf的“延迟写”策略

0. 运行库提供的IO读写函数采用“延迟写”策略的原因编程时经常会用到printf()函数&#xff0c;但是由于printf()函数涉及到和显示器或磁盘等外设进行交互&#xff0c;所以操作涉及到从“用户态–>内核态–>返回用户态”的一系列内核转换过程&#xff0c;但是从用户态通过…

MMORPG手游合理的性能参数

1&#xff09;MMORPG手游合理的性能参数 ​2&#xff09;使用ScriptableBuildPipeline打包的疑问 3&#xff09;如何获取到Animation修改材质球颜色后的颜色值 4&#xff09;嵌套预设AssetBundle打包的疑问 5&#xff09;LWRP渲染下&#xff0c;Profiler中函数开销高 这是第219…

缓冲技术之二:缓冲池BufferPool的简单实现

在文章缓冲技术中提到无论是单缓冲、双缓冲或循环缓冲&#xff0c;均仅是进程专属缓冲配备&#xff0c;而一旦考虑到操作系统的分时并行特性&#xff0c;任一时刻只有一个进程的缓冲体系在工作之中&#xff0c;而其他进程的缓冲体系并不在工作&#xff08;要么是迁移到swap cac…

运用Post Processing导致帧率明显下降

1&#xff09;运用Post Processing导致帧率明显下降 ​2&#xff09;RectMask2D是不是会频繁触发SendWillRenderCanvases 3&#xff09;Unity3D Sence为何一直处于已修改状态 4&#xff09;Sprite Atlas打Bundle的冗余问题 5&#xff09;IL2CPP在Xcode下增量编译问题 这是第220…

缓冲技术之三:Linux下I/O操作buffer缓冲块使用流程

0. Linux下缓冲池技术的简单介绍Linux文件系统中&#xff0c;存在着著名的三大缓冲技术用以提升读写操作效率&#xff1a; inode缓冲区、dentry缓冲区、块缓冲。其中所谓的块缓冲便是我们前面一直在讨论的缓冲池技术&#xff0c;常用来配备IO操作&#xff0c;用来减少IO读取次数…

UWA学堂|​移动游戏开发核心技术讲解:3D UI、GPU Skinning和DOTS

对3D UI制作、GPU Skinning和DOTS等技术感兴趣的Unity开发者&#xff0c;对游戏制作感兴趣的在校学生或是行业技术新人&#xff0c;本期这门课程《移动游戏开发核心技术讲解&#xff1a;3D UI、GPU Skinning和DOTS》你值得拥有&#xff0c;三位讲师分别讲解了对应的三个模块&am…