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

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

前面讲解到其实Semaphore的完整结构体内容应该如下

typedef struct{
   atomic_t  count;
   int sleepers;
   wait_queue_head_t  wait;
} semaphore;

在启用非阻塞机制下,可以在Semaphores耗光资源计数的情况下,进入主动休眠状态,而非空等,从而可以集中计算资源给当前正处在临界区的线程(进程),所以该机制在临界区操作耗时较长的情况下是很有用的。Pthread库封装的Semaphores实现了阻塞和非阻塞请求的两种机制

下面可以单独看看pthread.h提供的semaphore相关的接口内容

typedef struct sem_t_ * sem_t;
#define PTW32_DLLPORT __declspec (dllexport)

PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,
                int pshared, //进程间共享标识,pthread win32位实现进程间semaphore共享,故而改位始终为0
                unsigned int value);//value为初始给semaphore配备的可用资源数

PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);

PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem); //非阻塞请求

PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem); //阻塞请求,空等模式

PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,
                 const struct timespec * abstime); //等待有限时间后返回

PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);//为semaphore增加1资源数

PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,
                     int count);//为semaphore增加count资源数

PTW32_DLLPORT int __cdecl sem_open (const char * name,
                int oflag,
                mode_t mode,
                unsigned int value);//采用semaphore的名字寻找同名的内核对象,在进程间共享semaphore才需要用到,故而pthread win32未实现

PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);
PTW32_DLLPORT int __cdecl sem_unlink (const char * name);
PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,
                int * sval);

test_time_pthread32_wait.cpp—“阻塞请求(空等)”模式下测试代码

#include <iostream>
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <time.h>
#include <stdlib.h>
#include <Windows.h>

#pragma comment(lib, "pthreadVC2.lib")  //必须加上这句

using namespace std;

sem_t sem;
const int g_Number = 50;
const int killTimeStep = 100000000;

void* Function_t(void* Param)
{
     sem_wait(&sem); //空等
     int i = killTimeStep;
     while(i--);
     pthread_t myid = pthread_self();
     printf("线程ID=%d ", myid);
     //cout<<"线程ID="<<static_cast<int>(myid)<<"线程编号= "<<(int)Param<<endl;
     cout<<"线程编号= "<<(int)Param<<endl;
     sem_post(&sem);
     return NULL;
}

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

     int ret = sem_init(&sem, 0, 0);
     if (!ret )
         cout<<"主线程创建信号量成功,但主线程并没有激活该信号量"<<endl;
     else
     {
         cout<<"主线程创建信号量失败"<<endl;
         exit(EXIT_FAILURE);
     }

     pthread_t pid[ g_Number] = {0};
     for (int i=0; i<g_Number; i++)
         pthread_create(&pid[i], NULL, Function_t,(void*)i);

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

     if (sem_post(&sem) != 0)
     {
         printf("主线程激活信号量失败\n");
         exit(EXIT_FAILURE);
     }
     printf("主线程激活了信号量,其他子线程可以开始使用。\n");

     for (int i=0; i<g_Number; i++)
         pthread_join(pid[i], NULL);

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

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

test_time_pthread32_trywait.cpp —“非阻塞请求”模式下测试代码

#include <iostream>
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <time.h>
#include <stdlib.h>
#include <Windows.h>

#pragma comment(lib, "pthreadVC2.lib")  //必须加上这句

using namespace std;

sem_t sem;
const int g_Number = 50;
const int killTimeStep = 100000000;

void* Function_t(void* Param)
{
     sem_trywait(&sem); //非阻塞请求
     int i = killTimeStep;
     while(i--);
     pthread_t myid = pthread_self();
     printf("立即线程 ID=%d ", myid);
     fflush(stdout);
     printf("立即线程-编号=%d\n",(int)Param); //这里受限于printf()此类IO的延迟写策略,导致了存在输出错行的可能性
     fflush(stdout);
     //cout<<"线程ID="<<static_cast<long int>(myid)<<"线程编号= "<<(int)Param<<endl;
     //cout<<"线程编号= "<<(int)Param<<endl;
     sem_post(&sem);
     return NULL;
}

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

     int ret = sem_init(&sem, 0, 0); //第二个参数代表进程间共享标示位,但是pthread win32并没有实现,所以一直为0;
                    //第三个参数代表给信号量初始分配的资源数,不像WINAPI CreateSemaphores(xxx)需要指定资源上限,pthread采用了默认资源上限
     if (!ret )
         cout<<"主线程创建try信号量成功,但主线程并没有激活该信号量"<<endl;
     else
     {
         cout<<"主线程创建信号量失败"<<endl;
         exit(EXIT_FAILURE);
     }

     pthread_t pid[ g_Number] = {0};
     for (int i=0; i<g_Number; i++)
         pthread_create(&pid[i], NULL, Function_t,(void*)i);

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

     if (sem_post(&sem) != 0)
     {
         printf("主线程激活信号量失败\n");
         exit(EXIT_FAILURE);
     }
     printf("主线程激活了信号量,其他子线程可以开始使用。\n");

     for (int i=0; i<g_Number; i++)
         pthread_join(pid[i], NULL);

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

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

Conclusion: 可以看到pthread Win32实现的semaphore提供的“非阻塞请求机制”比“阻塞请求”在临界区操作耗时的场景下可以显著加速程序运行,提高性能,这点在并行计算中要重点使用。


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

相关文章

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…

STL工具库使用解析系列之一 STL::map

STL是C提供标准模块库的缩写&#xff0c;其中涉及多诸多常用的数据结构和算法&#xff0c;本文便是介绍map结构&#xff0c;一种和hash-table同样使用键值对的数据结构&#xff0c;但和哈希表常用数组实现不同&#xff0c;STL::map内核数据结构是红黑树&#xff0c;故而相比于h…