信号量实例—互斥
//account.h #ifndef _ACCOUNT_H #define _ACCOUNT_H typedef struct{ int code; double balance; //定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁(保护)的 /* 建议一把互斥锁和一个共享资源(银行账户)绑定,尽量不要设置成全局变量,否则可能出现 一把互斥锁去锁几百个账户(即一个线程获得锁,其他线程将阻塞),导致并发性的降低 */ int semid; }Account; //取款 extern double get_momney(Account *a, double momney); //存款 extern double save_momney(Account *a, double momney); //获得余额 extern double get_balance(Account *a); #endif
//account.c #include "account.h" #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include "pv.h" //取款 extern double get_momney(Account *a, double momney) { if(a == NULL){ printf("get_momney failed\n"); } P(a->semid, 0, 1); if(momney < 0 || a->balance < momney) { printf("momney not enough\n"); V(a->semid, 0, 1); return 0.0; } double balance = a->balance; sleep(1); balance = balance - momney; a->balance = balance; V(a->semid, 0, 1); return momney; } //存款 extern double save_momney(Account *a, double momney) { if(a == NULL){ printf("save_momney failed\n"); } P(a->semid, 0, 1); if(momney < 0){ V(a->semid, 0, 1); return 0.0; } double balance = a->balance; sleep(1); balance = balance + momney; a->balance = balance; V(a->semid, 0, 1); return momney; } //获得余额 extern double get_balance(Account *a) { if(a == NULL){ printf("get_balance failed\n"); } P(a->semid, 0, 1); double balance = a->balance; V(a->semid, 0, 1); return balance; }
//pv.h #ifndef _PV_H_ #define _PV_H_ #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> //int semget(key_t key, int nsems, int semflg) //初始化信号量集 extern int I(int nsems, int value); //P操作 extern void P(int semid, int semno, int value); //V操作 extern void V(int semid, int semno, int value); //删除信号量集 extern void D(int semid); #endif
//pv.c #include "pv.h" #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> #include <stddef.h> #include <stdio.h> union semun{//根据cmd来确定联合体中的值,只能有一个 int val; struct semid_ds *buf; unsigned short *array; }; //int semget(key_t key, int nsems, int semflg) //创建并初始化信号量集 int I(int nsems, int value) { key_t key = ftok(".", 0); int semid = semget(key, nsems, IPC_CREAT|IPC_EXCL|0777);//nsems:表示信号量的个数 if(semid < 0){ perror("semget failed"); return -1; } //int semctl(int semid,int semnum,int cmd,.../*union semun arg*/) unsigned short array[nsems]; union semun u; for(int i = 0; i < nsems; i++){ array[i] = value; } u.array = array;//信号量的初值 if(semctl(semid, 0, SETALL, u) < 0){//0:表示对信号量集中的所有信号 SETALL:表示对信号量集中的所有信号量赋初值 perror("semctl failed"); return -1; } return semid; } //P操作,对信号量集中的第semno的信号量作p操作,semno:0表示第一个信号量 void P(int semid, int semno, int value) { //int semop(int semid,struct sembuf *sops,size_t nsops) /*struct sembuf{ unsigned short sem_num;//信号量集中信号量的编号(即哪个信号量) short sem_op;//正数为V操作(加操作,1为加1操作,2为加2操作),负数为P操作(减操作,-1为减1操作,-2为减2操作) short sem_flg; } */ struct sembuf sb[] = {{semno, -value, SEM_UNDO}};//semno:0表示第一个信号量 if(semop(semid, sb, sizeof(sb)/sizeof(struct sembuf)) < 0){ perror("semopp failed"); } } //V操作 void V(int semid, int semno, int value) { struct sembuf sb[] = {{semno, value, SEM_UNDO}}; if(semop(semid, sb, sizeof(sb)/sizeof(struct sembuf)) < 0){ perror("semopv failed"); } } //删除信号量集 void D(int semid) { if(semctl(semid, 0, IPC_RMID, NULL) < 0){ perror("semctl failed"); } }
//account_test.c #include <stdio.h> #include "account.h" #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include "pv.h" #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t pid ; key_t key = ftok("/etc/passwd",1); int shmid = shmget(key, sizeof(Account), IPC_CREAT|IPC_EXCL|0777); Account *a = (Account *)shmat(shmid, NULL, 0); a->code = 1001; a->balance = 10000.0; a->semid = I(1, 1); pid = fork(); if(pid < 0){ perror("creat fork"); }else if(pid == 0){//child process double money = get_momney(a, 10000); printf("child get %d from %f\n",a->code, money); printf("get balance = %f\n",get_balance(a)); shmdt(a); }else{//father process double money = get_momney(a, 10000); printf("father get %d from %f\n",a->code, money); printf("get balance = %f\n",get_balance(a)); wait(NULL); D(a->semid); shmdt(a); shmctl(shmid, IPC_RMID,NULL); } return 0; }