信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。

以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。

在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

Python:

原锁是一个同步原语,当它锁住时不归某个特定的线程所有。在Python中,它是目前可用的最底层的同步原语,直接通过thread的扩展模块实现。

一个原锁处于“locked”或者“unlocked”状态中的一种。它创建时处于unlocked状态。它有两个基本的方法,acquire()和release()。当状态是unlocked时,acquire()改变该状态为locked并立即返回。当状态是locked时,acquire()将阻塞直至在另外一个线程中调用release()来将它变为unlocked,然后acquire()调用将它重置为locked并返回。release()方法应该只在locked状态下调用;它改变状态为unlocked并立即返回。如果尝试是否一个unlocked的锁,将引发一个ThreadError。

#!/usr/bin/python3

import threading
import time

def handler(n,sem):
    sem.acquire()
    time.sleep(1)
    print("Start the No.%d thread"%n)
    sem.release()
sem = threading.Semaphore(5)

for i in range(5):
    t = threading.Thread(target=handler,args=(i,sem))
    t.start()

C:

Linux下关于信号量结构体表示为:sem_t

      操作结构体的函数:

      初始化函数: sem_init(sem_t * __sem,int __pshared,unsigned int __value);

      触发信号量值:sem_post(sem_t * __sem);

      等待信号量触发:

      一直等待:sem_wait(sem_t * __sem);  

      测试sem是否触发:sem_trywait(sem_t * __sem); 

      等待超时:sem_timedwait(sem_t * __restrict __sem, __ const struct timespec * __restrict __abstime);

      释放销毁信号量:

      sem _destroy(sem_t * __sem);

还有一个很重要的函数是semctl,具体可以用man semctl进行查看

int semctl(int semid, int semnum, int cmd, ...);

DESCRIPTION
       semctl()  performs  the  control operation specified by cmd on the Sys‐
       tem V semaphore set identified by semid, or on the semnum-th  semaphore
       of that set.  (The semaphores in a set are numbered starting at 0.)

       This  function  has  three  or  four arguments, depending on cmd.  When
       there are four, the fourth has the type union semun.  The calling  pro‐
       gram must define this union as follows:

           union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           };

       The semid_ds data structure is defined in <sys/sem.h> as follows:

           struct semid_ds {
               struct ipc_perm sem_perm;  /* Ownership and permissions */
               time_t          sem_otime; /* Last semop time */
               time_t          sem_ctime; /* Last change time */
               unsigned long   sem_nsems; /* No. of semaphores in set */
           };

     在 Linux 下,PV 操作通过调用semop函数来实现。该函数定义在头文件 sys/sem.h中,原型如下:

     int  semop(int  semid,struct sembuf  *sops,size_t nsops);

     函数的参数 semid 为信号量集的标识符;参数 sops 指向进行操作的结构体数组的首地址;参数 nsops 指出将要进行操作的信号的个数。

    semop 函数调用成功返回 0,失败返回 -1。

    semop 的第二个参数 sops 指向的结构体数组中,每个 sembuf 结构体对应一个特定信号的操作。因此对信号量进行操作必须熟悉该数据结构,该结构定义在 linux/sem.h,如下所示:

     struct  sembuf{

         unsigned short   sem_num;      //信号在信号集中的索引,0代表第一个信号,1代表第二个信号  

         short            sem_op;      //操作类型

         short            sem_flg;    //操作标志

     };

#include "head4sem.h"

int main(int argc, char **argv)
{
	key_t key1 = ftok(PROJ_PATH, ID4SHM);
	key_t key2 = ftok(PROJ_PATH, ID4SEM);


	int shmid = shmget(key1, SHMSZ, IPC_CREAT|0644);
	char *shmaddr = shmat(shmid, NULL, 0);


	int semid = semget(key2, 2, IPC_CREAT|IPC_EXCL|0644);
	if(semid == -1 && errno == EEXIST)
	{
		semid = semget(key2, 2, 0644);
	}
	else
	{
		seminit(semid, 0, 0);
		seminit(semid, 1, 1);
	}
	
	while(1)
	{
		sem_p(semid, 1);
		fgets(shmaddr, SHMSZ, stdin);
		sem_v(semid, 0);
	}

	return 0;
}

 

#include "head4sem.h"

int main(int argc, char **argv)
{
	key_t key1 = ftok(PROJ_PATH, ID4SHM);
	key_t key2 = ftok(PROJ_PATH, ID4SEM);


	int shmid = shmget(key1, SHMSZ, IPC_CREAT|0644);
	char *shmaddr = shmat(shmid, NULL, 0);


	int semid = semget(key2, 2, IPC_CREAT|IPC_EXCL|0644);
	if(semid == -1 && errno == EEXIST)
	{
		semid = semget(key2, 2, 0644);
	}
	else
	{
		seminit(semid, 0, 0);
		seminit(semid, 1, 1);
	}
	
	while(1)
	{
		sem_p(semid, 1);
		fgets(shmaddr, SHMSZ, stdin);
		sem_v(semid, 0);
	}

	return 0;
}
#ifndef _HEAD4SEM_H_
#define _HEAD4SEM_H_

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>

#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define SHMSZ 128

#define PROJ_PATH "."
#define ID4SHM 1
#define ID4SEM 2

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	struct seminfo *__buf;
};

static void sem_p(int semid, int semnum)
{
	struct sembuf op[1];
	op[0].sem_num = semnum;
	op[0].sem_op  = -1;
	op[0].sem_flg = 0;

	semop(semid, op, 1);
}

static void sem_v(int semid, int semnum)
{
	struct sembuf op[1];
	op[0].sem_num = semnum;
	op[0].sem_op  = 1;
	op[0].sem_flg = 0;

	semop(semid, op, 1);
}

static void seminit(int semid, int semnum, int value)
{
	union semun a;
	a.val = value;
	semctl(semid, semnum, SETVAL, a);
}

#endif