在本文中,您将会了解到关于SystemVIPC之信号量的新资讯,同时我们还将为您解释ipcs信号量删除的相关在本文中,我们将带你探索SystemVIPC之信号量的奥秘,分析ipcs信号量删除的特点,并
在本文中,您将会了解到关于System V IPC 之信号量的新资讯,同时我们还将为您解释ipcs信号量删除的相关在本文中,我们将带你探索System V IPC 之信号量的奥秘,分析ipcs信号量删除的特点,并给出一些关于3. System V IPC、c – posix信号量的Sys V SEM_UNDO等价物、C#并行编程之信号量、c++线程同步之信号量的实用技巧。
本文目录一览:System V IPC 之信号量(ipcs信号量删除)
》之后接着介绍 System V IPC 的信号量编程。在开始正式的内容前让我们先概要的了解一下 Linux 中信号量的分类。
- ",而 System V 信号量的引用头文件是 "
"。
解决互斥共享资源的同步问题而引入的机制。信号量的实质是整数计数器,其中记录了可供访问的共享资源的单元个数。本文接下来提到的信号量都特指 System V IPC 信号量。
sem_perm:对应于该信号量集的 ipc_perm 结构(该结构的详情请参考《System V IPC 之内存共享》)指针。sem_base:sem 结构指针,指向信号量集中第一个信号量的 sem 结构。sem_nsems:信号量集中信号量的个数。sem_otime:最近一次调用 semop 函数的时间。sem_ctime:最近一次改变该信号量集的时间。
<span>int semget(key_t key,<span>int nsems,<span>int semflg);
<span>int semop(<span>int semid,<span>struct sembuf *sops,size_t nsops);
sem_op > 0:表示进程对资源使用完毕,交回该资源。此时信号量集的 semid_ds 结构的 sem_base.semval 将加上 sem_op 的值。若此时设置了 SEM_UNDO 位,则信号量的调整值将减去 sem_op 的绝对值。sem_op = 0:表示进程要等待,直至 sem_base.semval 变为 0。sem_op < 0:表示进程希望使用资源。此时将比较 sem_base.semval 和 sem_op 的绝对值大小。如果 sem_base.semval 大于等于 sem_op 的绝对值,说明资源足够分配给此进程,则 sem_base.semval 将减去 sem_op 的绝对值。若此时设置了 SEM_UNDO 位,则信号量的调整值将加上 sem_op 的绝对值。如果 sem_base.semval 小于 sem_op 的绝对值,表示资源不足。若设置了 IPC_NOWAIT 位,则函数出错返回,否则 semid_ds 结构中的 sem_base.semncnt 加 1,进程等待直至 sem_base.semval 大于等于 sem_op 的绝对值或该信号量被删除。
<span>int semctl(<span>int semid,<span>int semnum,<span>int cmd,union semun arg);
GETALL:获得 semid 所表示的信号量集中信号量的个数,并将该值存放在无符号短整型数组 array 中。GETNCNT:获得 semid 所表示的信号量集中的等待给定信号量锁的进程数目,即 semid_ds 结构中 sem.semncnt 的值。GETPID:获得 semid 所表示的信号量集中最后一个使用 semop 函数的进程 ID,即 semid_ds 结构中的 sem.sempid 的值。GETVAL:获得 semid 所表示的信号量集中 semunm 所指定信号量的值。GETZCNT:获得 semid 所表示的信号量集中的等待信号量成为 0 的进程数目,即 semid_ds 结构中的 sem.semncnt 的值。IPC_RMID:删除该信号量。IPC_SET:按参数 arg.buf 指向的结构中的值设置该信号量对应的 semid_ds 结构。只有有效用户 ID 和信号量的所有者 ID 或创建者 ID 相同的用户进程,以及超级用户进程可以执行这一操作。IPC_STAT:获得该信号量的 semid_ds 结构,保存在 arg.buf 指向的缓冲区。SETALL:以 arg.array 中的值设置 semid 所表示的信号量集中信号量的个数。SETVAL:设置 semid 所表示的信号量集中 semnum 所指定信号量的值。
<span>void client(<span>int shmid,<span>int
<span>void delete(<span>void
<span>void unlocksem(<span>int semid,<span>int<span> semnum);
<span>void clientwrite(<span>int shmid,<span>int semid,<span>char <span>buffer);
union semun
{
<span>int<span> val;
<span>struct semid_ds <span>buf;
<span>ushort <span>array;
};
<span>int safesemget(key_t key,<span>int nssems,<span>int<span> semflg);
<span>int safesemctl(<span>int semid,<span>int semunm,<span>int<span> cmd,union semun arg);
<span>int safesemop(<span>int semid,<span>struct sembuf <span>sops,unsigned nsops);
<span>int safeshmget(key_t key,<span>int size,<span>int<span> shmflg);
<span>void safeshmat(<span>int shmid,<span>const <span>void shmaddr,<span>int<span> shmflg);
<span>int safeshmctl(<span>int shmid,<span>struct shmid_ds *<span>buf);
<span>int main(<span>int argc,<span>char *<span>argv)
{
<span>if(argc < <span>3<span>){
server();
}
<span>else<span>{
client(atoi(argv[<span>1]),atoi(argv[<span>2<span>]));
}
<span>return <span>0<span>;
}
<span>void server(<span>void<span>)
{
union semun sunion;
<span>int<span> semid,shmid;
<span>char *<span>buffer;
semid = safesemget(IPC_PRIVATE,<span>2,SHM_R|<span>SHM_W);
deleteSemid =<span> semid;
<span>//<span> 在服务器端程序退出时删除掉信号量集。
atexit(&<span>delete);
signal(SIGINT,&<span>sigdelete);
<span>//<span> 把第一个信号量设置为 1,第二个信号量设置为 0,<span>//<span> 这样来控制:必须在客户端程序把数据写入共享内存后服务器端程序才能去读共享内存
sunion.val = <span>1<span>;
safesemctl(semid,SN_EMPTY,SETVAL,sunion);
sunion.val = <span>0<span>;
safesemctl(semid,SN_FULL,sunion);
shmid = safeshmget(IPC_PRIVATE,SHMDATASIZE,IPC_CREAT|SHM_R|<span>SHM_W);
buffer = safeshmat(shmid,<span>0,<span>0<span>);
safeshmctl(shmid,IPC_RMID,NULL);
<span>//<span> 打印共享内存 ID 和 信号量集 ID,客户端程序需要用它们作为参数
printf(<span>"<span>Server is running with SHM id %d\n<span>"<span>,shmid);
printf(<span>"<span>Server is running with SEM id %d\n<span>"<span>,semid);
<span>while(<span>1<span>)
{
printf(<span>"<span>Waiting until full...<span>"<span>);
fflush(stdout);
locksem(semid,SN_FULL);
printf(<span>"<span>done.\n<span>"<span>);
printf(<span>"<span>Message received: %s.\n<span>"<span>,buffer);
unlocksem(semid,SN_EMPTY);
}
}
<span>void client(<span>int shmid,<span>int<span> semid)
{
<span>char *<span>buffer;
buffer = safeshmat(shmid,<span>0<span>);
printf(<span>"<span>Client operational: shm id is %d,sem id is %d\n<span>"<span>,shmid,semid);
<span>while(<span>1<span>)
{
<span>char input[<span>3<span>];
printf(<span>"<span>\n\nMenu\n1.Send a message\n<span>"<span>);
printf(<span>"<span>2.Exit\n<span>"<span>);
fgets(input,<span>sizeof<span>(input),stdin);
<span>switch(input[<span>0<span>])
{
<span>case <span>'<span>1<span>'<span>:
clientwrite(shmid,semid,buffer);
<span>break<span>;
<span>case <span>'<span>2<span>'<span>:
exit(<span>0<span>);
<span>break<span>;
}
}
}
…
<span>void locksem(<span>int semid,<span>int<span> semnum)
{
<span>struct<span> sembuf sb;
sb.sem_num =<span> semnum;
sb.sem_op = -<span>1<span>;
sb.sem_flg =<span> SEM_UNDO;
safesemop(semid,&sb,<span>1<span>);
}
<span>void unlocksem(<span>int semid,<span>int<span> semnum)
{
<span>struct<span> sembuf sb;
sb.sem_num =<span> semnum;
sb.sem_op = <span>1<span>;
sb.sem_flg =<span> SEM_UNDO;
safesemop(semid,<span>1<span>);
}
…
代码保存到文件 sem.c 文件中,并编译:
输出的 SHM id 和 SEM id 作为参数传入到客户端程序中:
代码)。
删除掉这个信号量集,以免给系统添加垃圾。
一个很简陋的应用共享内存的 demo,由于没有应用任何的同步访问技术,其输出是比较混乱的。本文的 demo 则是在其基础上添加了信号量来控制进程对共享内存的访问。从程序的输出我们可以看到,使用信号量解决互斥共享资源的同步问题后,服务器端程序的输出变得和客户端的输入一致了。
3. System V IPC
System V IPC包含三种类型的IPC:System V消息队列、System V信号量、System V共享内存区
1. key_t键和ftok函数
三种类型的System V IPC使用key_t值作为名字。头文件<sys/types.h>定义key_t为一个至少32位的整数。函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键
#include <sys/ipc.h> key_t ftok(const char *pathname, int id);
ftok的典型实现调用stat函数,然后组合三个值:stat结构的st_dev信息、stat结构的st_info信息、id的低序8位(不能为0)。不能保证不同的路径名与同一个id的组合产生不同的键。
2. ipc_perm结构
内核给每个IPC对象维护一个信息结构,内容跟内核给文件维护的信息类似
struct ipc_perm {
uid_t uid; /* owner’s user id */
gid_t gid; /* owner’s group id */
uid_t cuid; /* creator’s user id */
gid_t cgid; /* creator’s group id */
mode_t mode; /* read-write permissions */
ulong_t seq; /* slot usage sequence number */
key_t key; /* IPC key */
};
3. 创建于打开IPC通道
创建或打开一个IPC对象的三个getXXX函数的第一个参数key有两种选择:调用ftok,传递pathname和id;指定key为IPC_PRIVATE,保证创建一个新的、唯一的IPC对象。System V IPC定义了自己的IPC_xxx常值
#include <sys/ipc.h> <sys/shm.h> int shmget(key_t key, size_t size, int oflag);
#include <sys/types.h> <sys/ipc.h> <sys/sem.h> int semget(key_t key, int nsems, int oflag);
#include <sys/types.h> <sys/ipc.h> <sys/msg.h> int msgget(key_t key, int oflag);
4. IPC权限
oflag参数某些位初始化ipc_perm结构的mode成员
一个进程可通过调用相应IPC机制ctlXXX函数(IPC_SET)修改属主ID,创建者ID却从不改变。三个ctlXXX函数允许一个进程修改某个IPC对象的mode成员。
每当有一个进程访问某个IPC对象,IPC执行两级检查:IPC对象被打开时执行一次,每次使用对象时执行一次
每当有一个进程以getXXX函数建立访问某个存在IPC对象的通道时,IPC执行一次初始检查,验证调用者的oflag参数没有指定不在该对象ipc_perm结构mode成员中的任何访问位。创建者和调用者的权限位一致。绕过这种检查的方法:如果已知IPC对象存在,指定一个值为0的oflag参数。
每次IPC操作都对使用该操作的进程执行一次权限测试:超级用户总是赋予访问权;如果当前进程的有效用户ID等于IPC对象的uid或cuid,而且相应的访问位(如果调用者要在IPC对象执行读操作,那么读位必须设置,如果要执行写操作,那么写位必须设置)在IPC对象的mode成员是打开的。
如果当前进程的有效组ID等于IPC对象的gid或cgid,而且相应访问位在IPC对象mode成员中是打开的
如果上面的测试没有一个为真,那么相应的“其他用户”访问位在IPC对象的mode成员中必须是打开的
5. 标识符重用
ipc_perm结构的seq变量是一个槽位使用情况序列号,一个由内核为系统每个潜在的IPC对象维护的计数器。每当删除一个IPC对象,内核就递增相应的槽位号,若溢出则循环回0。System V IPC标识符是系统范围的,不是特定于进程的。从某个getXXX函数获得一个IPC标识符适用于所有进程。可能导致某些进程尝试不同的小整数标识符,期待找出一个当前允许大家读访问的消息队列。解决办法是把标识符值可能范围扩大到所有整数:每次重用一个IPC表项,把返回给调用者的标识符值增加一个IPC表项数。递增槽位使用情况序列号的另一个原因是避免短时间内重用System V IPC标识符,有助于确保过早终止的服务器重新启动后不会重用标识符。
6. ipcs和ipcrm程序
由于System V IPC的三种类型不是以文件系统中的路径名标识的,提供两个特殊程序ipcs和ipcrm。ipcs输出有关System V IPC特性的各种信息,ipcrm删除一个System V消息队列、信号量或共享内存区
c – posix信号量的Sys V SEM_UNDO等价物
解决方法
// set handler signal(SIGSEGV,handler); void handler(int signum) { // unlock the locked semaphores signal(SIGSEGV,SIG_DFL); }
C#并行编程之信号量
一:CountdownEvent
这种采用信号状态的同步基元非常适合在动态的fork,join的场景,它采用“信号计数”的方式,就比如这样,一个麻将桌只能容纳4个人打麻将,如果后来的人也想搓一把碰碰运气,那么他必须等待直到麻将桌上的人走掉一位。好,这就是简单的信号计数机制,从技术角度上来说它是定义了最多能够进入关键代码的线程数。
但是CountdownEvent更牛X之处在于我们可以动态的改变“信号计数”的大小,比如一会儿能够容纳8个线程,一下又4个,一下又10个,这样做有什么好处呢?还是承接上一篇文章所说的,比如一个任务需要加载1w条数据,那么可能出现这种情况。
加载User表:根据user表的数据量,我们需要开5个task。
加载Product表:产品表数据相对比较多,计算之后需要开8个task。
加载order表:由于我的网站订单丰富,计算之后需要开12个task。
先前的文章也说了,我们需要协调task在多阶段加载数据的同步问题,那么如何应对这里的5,8,12,幸好,CountdownEvent给我们提供了可以动态修改的解决方案。
我们看到有两个主要方法:Wait和Signal。每调用一次Signal相当于麻将桌上走了一个人,直到所有人都搓过麻将wait才给放行,这里同样要注意也就是“超时“问题的存在性,尤其是在并行计算中,轻量级别给我们提供了”取消标记“的机制,这是在重量级别中不存在的,比如下面的重载public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken),具体使用可以看前一篇文章的介绍。
//默认的容纳大小为“硬件线程“数 static CountdownEvent cde = new CountdownEvent(Environment.ProcessorCount); static void Main(string[] args) { //加载User表需要5个任务 var userTaskCount = 5; //重置信号 cde.Reset(userTaskCount); for (int i = 0; i < userTaskCount; i++) { Task.Factory.StartNew((obj) => { LoadUser(obj); }, i); } //等待所有任务执行完毕 cde.Wait(); Console.WriteLine("\nUser表数据全部加载完毕!\n"); //加载product需要8个任务 var productTaskCount = 8; //重置信号 cde.Reset(productTaskCount); for (int i = 0; i < productTaskCount; i++) { Task.Factory.StartNew((obj) => { LoadProduct(obj); }, i); } cde.Wait(); Console.WriteLine("\nProduct表数据全部加载完毕!\n"); //加载order需要12个任务 var orderTaskCount = 12; //重置信号 cde.Reset(orderTaskCount); for (int i = 0; i < orderTaskCount; i++) { Task.Factory.StartNew((obj) => { LoadOrder(obj); }, i); } cde.Wait(); Console.WriteLine("\nOrder表数据全部加载完毕!\n"); Console.WriteLine("\n(*^__^*) 嘻嘻,恭喜你,数据全部加载完毕\n"); Console.Read(); } static void LoadUser(object obj) { try { Console.WriteLine("当前任务:{0}正在加载User部分数据!", obj); } finally { cde.Signal(); } } static void LoadProduct(object obj) { try { Console.WriteLine("当前任务:{0}正在加载Product部分数据!", obj); } finally { cde.Signal(); } } static void LoadOrder(object obj) { try { Console.WriteLine("当前任务:{0}正在加载Order部分数据!", obj); } finally { cde.Signal(); } }
二:SemaphoreSlim
在.net 4.0之前,framework中有一个重量级的Semaphore,人家可以跨进程同步,咋轻量级不行,msdn对它的解释为:限制可同时访问某一资源或资源池的线程数。关于它的重量级demo,我的上一个系列有演示,你也可以理解为CountdownEvent是 SemaphoreSlim的功能加强版,好了,举一个轻量级使用的例子。
static SemaphoreSlim slim = new SemaphoreSlim(Environment.ProcessorCount, 12); static void Main(string[] args) { for (int i = 0; i < 12; i++) { Task.Factory.StartNew((obj) => { Run(obj); }, i); } Console.Read(); } static void Run(object obj) { slim.Wait(); Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj); //这里busy3s中 Thread.Sleep(3000); slim.Release(); }
同样,防止死锁的情况,我们需要知道”超时和取消标记“的解决方案,像SemaphoreSlim这种定死的”线程请求范围“,其实是降低了扩展性,所以说,试水有风险,使用需谨慎,在觉得有必要的时候使用它。
三: ManualResetEventSlim
相信它的重量级别大家都知道是ManualReset,而这个轻量级别采用的是"自旋等待“+”内核等待“,也就是说先采用”自旋等待的方式“等待,直到另一个任务调用set方法来释放它。如果迟迟等不到释放,那么任务就会进入基于内核的等待,所以说如果我们知道等待的时间比较短,采用轻量级的版本会具有更好的性能,原理大概就这样,下面举个小例子。
//2047:自旋的次数 static ManualResetEventSlim mrs = new ManualResetEventSlim(false, 2047); static void Main(string[] args) { for (int i = 0; i < 12; i++) { Task.Factory.StartNew((obj) => { Run(obj); }, i); } Console.WriteLine("当前时间:{0}我是主线程{1},你们这些任务都等2s执行吧:\n", DateTime.Now, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); mrs.Set(); } static void Run(object obj) { mrs.Wait(); Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj); }
到此这篇关于C#并行编程之信号量的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。
- 深入了解c# 信号量和互斥体
- C#信号量用法简单示例
- C#并行编程之PLINQ(并行LINQ)
- C#并行编程之Task同步机制
- C#并行编程之Task任务
- C#并行编程之数据并行Tasks.Parallel类
c++线程同步之信号量
// MutexExDlg.h : 头文件
//
#pragma once
// CMutexExDlg 对话框
class CMutexExDlg : public CDialogEx
{
// 构造
public:
CMutexExDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_MUTEXEX_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
static HANDLE hThread[3];
static HANDLE m_Semaphore;
static DWORD WINAPI ThreadProc0(LPVOID lpParameter);
static DWORD WINAPI ThreadProc1(LPVOID lpParameter);
static DWORD WINAPI ThreadProc2(LPVOID lpParameter);
static DWORD WINAPI ThreadProc3(LPVOID lpParameter);
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnBnClickedButton1();
static int m_Edit0;
static int m_Edit1;
static int m_Edit2;
static int m_Edit3;
};
// MutexExDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "MutexEx.h"
#include "MutexExDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CMutexExDlg 对话框
CMutexExDlg::CMutexExDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CMutexExDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMutexExDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, m_Edit0);
DDV_MinMaxInt(pDX, m_Edit0, 0, 1000);
DDX_Text(pDX, IDC_EDIT2, m_Edit1);
DDV_MinMaxInt(pDX, m_Edit1, 0, 1000);
DDX_Text(pDX, IDC_EDIT3, m_Edit2);
DDV_MinMaxInt(pDX, m_Edit2, 0, 1000);
DDX_Text(pDX, IDC_EDIT4, m_Edit3);
DDV_MinMaxInt(pDX, m_Edit3, 0, 1000);
}
BEGIN_MESSAGE_MAP(CMutexExDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CMutexExDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
// CMutexExDlg 消息处理程序
BOOL CMutexExDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMutexExDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMutexExDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
// 重写虚函数 PreTranslateMessage 屏蔽掉Esc键和Enter键
BOOL CMutexExDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
int keyCode = (int)pMsg->wParam;
if (keyCode == VK_ESCAPE || keyCode == VK_RETURN)
{
return TRUE;
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
HANDLE CMutexExDlg::m_Semaphore = NULL;
int CMutexExDlg::m_Edit0 = 0;
int CMutexExDlg::m_Edit1 = 0;
int CMutexExDlg::m_Edit2 = 0;
int CMutexExDlg::m_Edit3 = 0;
HANDLE CMutexExDlg::hThread[3] = { NULL };
// 抢红包按钮点击事件处理函数
void CMutexExDlg::OnBnClickedButton1()
{
// 获取编辑框内容到str变量
CString str;
GetDlgItem(IDC_EDIT1)->GetWindowText(str);
// CString 转 int
m_Edit0 = _ttoi(str);
m_Edit1 = 0;
m_Edit2 = 0;
m_Edit3 = 0;
// 这里需要创建一个线程 因为 WaitForMultipleObjects 会阻塞住 另外把this指针作为参数传递给线程,用于子线程更新编辑框内容
HANDLE hThread0 = ::CreateThread(NULL, NULL, ThreadProc0, this, NULL, NULL);
CloseHandle(hThread0);
}
DWORD WINAPI CMutexExDlg::ThreadProc0(LPVOID lpParameter)
{
CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter;
// 创建一个信号量
m_Semaphore = ::CreateSemaphore(NULL,
0, // 表示初始资源数量,0时不发送信号
3, // 表示最大并发数量 lInitialCount < lMaximumCount
NULL);
// 创建三个线程抢红包
hThread[0] = ::CreateThread(NULL, NULL, ThreadProc1, lpParameter, NULL, NULL);
hThread[1] = ::CreateThread(NULL, NULL, ThreadProc2, lpParameter, NULL, NULL);
hThread[2] = ::CreateThread(NULL, NULL, ThreadProc3, lpParameter, NULL, NULL);
// 设置编辑框内容
::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT1), L"1000");
// 将对象设置为已通知状态
ReleaseSemaphore(m_Semaphore,
1, // 表示增加个数,必须大于0且不超过最大资源数量
NULL);
// 使用WaitForMultipleObjects监听所有线程,当线程全部结束后,调用CloseHandle关闭句柄.
WaitForMultipleObjects(3, hThread, TRUE, -1);
::CloseHandle(hThread[0]);
::CloseHandle(hThread[1]);
::CloseHandle(hThread[2]);
::CloseHandle(m_Semaphore);
return 0;
}
// 线程回调函数1
DWORD WINAPI CMutexExDlg::ThreadProc1(LPVOID lpParameter)
{
CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter;
TCHAR szBuffer[10];
DWORD dwTimer = 0;
WaitForSingleObject(m_Semaphore, INFINITE);
while (dwTimer < 100)
{
Sleep(50);
memset(szBuffer, 0, 10);
::GetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT2), szBuffer, 10);
swscanf(szBuffer, L"%d", &dwTimer);
dwTimer++;
memset(szBuffer, 0, 10);
swprintf(szBuffer, L"%d", dwTimer);
::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT2), szBuffer);
}
// 递增信号量的当前资源计数
ReleaseSemaphore(m_Semaphore, 1, NULL);
return 0;
}
// 线程回调函数2
DWORD WINAPI CMutexExDlg::ThreadProc2(LPVOID lpParameter)
{
CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter;
TCHAR szBuffer[10];
DWORD dwTimer = 0;
WaitForSingleObject(m_Semaphore, INFINITE);
while (dwTimer < 100)
{
Sleep(50);
memset(szBuffer, 0, 10);
::GetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT3), szBuffer, 10);
// 将TCHAR* 转换为 int
swscanf(szBuffer, L"%d", &dwTimer);
dwTimer++;
memset(szBuffer, 0, 10);
// int 转换为TCHAR*
swprintf(szBuffer, L"%d", dwTimer);
::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT3), szBuffer);
}
// 递增信号量的当前资源计数
ReleaseSemaphore(m_Semaphore, 1, NULL);
return 0;
}
// 线程回调函数3
DWORD WINAPI CMutexExDlg::ThreadProc3(LPVOID lpParameter)
{
CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter;
TCHAR szBuffer[10];
DWORD dwTimer = 0;
WaitForSingleObject(m_Semaphore, INFINITE);
while (dwTimer < 100)
{
Sleep(50);
memset(szBuffer, 0, 10);
::GetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT4), szBuffer, 10);
swscanf(szBuffer, L"%d", &dwTimer);
dwTimer++;
memset(szBuffer, 0, 10);
swprintf(szBuffer, L"%d", dwTimer);
::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT4), szBuffer);
}
// 递增信号量的当前资源计数
ReleaseSemaphore(m_Semaphore, 1, NULL);
return 0;
}
今天关于System V IPC 之信号量和ipcs信号量删除的分享就到这里,希望大家有所收获,若想了解更多关于3. System V IPC、c – posix信号量的Sys V SEM_UNDO等价物、C#并行编程之信号量、c++线程同步之信号量等相关知识,可以在本站进行查询。
本文标签: