90-互斥量的共享属性

用于线程互斥的互斥量也有相应的属性 pthread_mutexattr_t,这里只讨论三个方面:

  • 共享属性
  • 鲁棒属性
  • 互斥量的递归类型

本文先介绍共享属性。

1. 属性的初始化与回收

互斥量属性的数据类型是 pthread_mutexattr_t. 下面两个函数分别用于互斥量属性的初始化与回收。

int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);

2. 共享属性

除了互斥量有共享属性外,其它的线程互斥同步对象如读写锁、自旋锁、条件变量、屏障都有共享属性。

该属性有两种情况:

  • PTHREAD_PROCESS_PRIVATE : 这种是默认的情况,表示互斥量只能在本进程内部使用。
  • PTHREAD_PROCESS_SHARED:表示互斥量可以在不同进程间使用。

对于第一种情况,我们早已经学会。第二种情况,需要结合前面的进程间通信技术才有用。一般需要在共享内存中分配互斥量,然后再为互斥量指定PTHREAD_PROCESS_SHARED属性就可以了。

3. 实验

本实验中的程序分为了 4 个部分,分别是 init、destroy、buyticket 和 rbstbuyticket.

init 程序的作用是申请共享内存,并在共享内存中分配互斥量等。
destroy 用来回收共享内存中的互斥量,并销毁共享内存。
buyticket 和 rbstbuyticket 是从共享内存的数据中抢票的。

.
|- init.c
|- destroy.c
|- buyticket.c
|- rbstbuyticket.c
|- Makefile // Makefile 文件主要用来一次性编译完所有文件。

本部分实验,只用的到前三个程序,最后一个 rbstbuyticket.c 在讲鲁棒属性的时候用的到。

3.1 init.c 程序

程序 init 用来创建一块共享内存,可以给 init 程序传递一个命令行参数,也可以不传。如果传递参数,表示为互斥量指定鲁棒性属性,本文我们先不传递参数。

// init.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>

#define PERR(msg) do { perror(msg); exit(-1); } while(0)
#define PPERR(err, msg) do { err = errno; perror(msg); exit(-1); } while(0)

struct ticket {
  int remain;
  pthread_mutex_t lock;
};

// 打印共享属性
void printshared(pthread_mutexattr_t *attr) {
  int err, shared;
  err = pthread_mutexattr_getpshared(attr, &shared);
  if (err != 0) PPERR(err, "pthread_mutexattr_getshared");
  if (shared == PTHREAD_PROCESS_PRIVATE)
    puts("shared = PTHREAD_PROCESS_PRIVATE");
  else if (shared == PTHREAD_PROCESS_SHARED)
    puts("shared = PTHREAD_PROCESS_SHARED");
  else
    puts("shared = ???");
}
// 打印鲁棒属性
void printrobust(pthread_mutexattr_t *attr) {
  int err, robust;
  err = pthread_mutexattr_getrobust(attr, &robust);
  if (err != 0) PPERR(err, "pthread_mutexattr_getrobust");
  if (robust == PTHREAD_MUTEX_STALLED)
    puts("robust = PTHREAD_MUTEX_STALLED");
  else if (robust == PTHREAD_MUTEX_ROBUST)
    puts("robust = PTHREAD_MUTEX_ROBUST");
  else
    puts("robust = ???");
}

int main(int argc, char* argv[]) {
  int err, shared, robust = 0, flag = 1;
  if (argc >= 2) robust = 1;
  key_t key = 0x8888;
  // 创建共享内存
  int id = shmget(key, getpagesize(), IPC_CREAT | IPC_EXCL | 0666);
  if (id < 0) PERR("shmget");

  // 挂接共享内存
  struct ticket *t = (struct ticket*)shmat(id, NULL, 0); 

  if ((int)t == -1) PERR("shmat");

  // 设置余票数量为 5
  t->remain = 5;

  pthread_mutexattr_t mutexattr;
  err = pthread_mutexattr_init(&mutexattr);
  if (err != 0) PPERR(err, "pthread_mutexattr_init");

  printshared(&mutexattr);
  printrobust(&mutexattr);

  // 将互斥量的共享属性设置为可以进程间共享使用。
  shared = PTHREAD_PROCESS_SHARED;
  err = pthread_mutexattr_setpshared(&mutexattr, shared);
  if (err != 0) PPERR(err, "pthread_mutexattr_setshared");

  // 如果有命令行参数,就将鲁棒性设置为 PTHREAD_MUTEX_ROBUST
  // 本文暂时不设置此值
  if (robust) {
    err = pthread_mutexattr_setrobust(&mutexattr, PTHREAD_MUTEX_ROBUST);
    if (err != 0) PPERR(err, "pthread_mutexattr_setshared");
  }

  puts("modify attribute ------------------>");
  printshared(&mutexattr);
  printrobust(&mutexattr);

  pthread_mutex_init(&t->lock, &mutexattr);

  err = pthread_mutexattr_destroy(&mutexattr);
  if (err != 0) PPERR(err, "pthread_mutexattr_destroy");

  err = shmdt((void*)t);

  if (err != 0) PERR("shmdt");

  return 0;
}

3.2 destroy.c

destroy 程序主要用于回收互斥量和共享内存。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>

#define PERR(msg) do { perror(msg); exit(-1); } while(0)
#define PPERR(err, msg) do { err = errno; perror(msg); exit(-1); } while(0)

struct ticket {
  int remain;
  pthread_mutex_t lock;
};

int main() {
  int err;
  key_t key = 0x8888;
  int id = shmget(key, 0, 0); 
  if (id < 0) PERR("shmget");

  struct ticket *t = (struct ticket*)shmat(id, NULL, 0); 

  if ((int)t == -1) PERR("shmat");

  err = pthread_mutex_destroy(&t->lock);
  if (err != 0) PPERR(err, "pthread_mutex_destroy");

  err = shmdt((void*)t);
  if (err != 0) PERR("shmdt");

  err = shmctl(id, IPC_RMID, NULL);
  if (err != 0) PERR("shmctl");


  return 0;
}

3.3 buyticket.c

buyticket 程序主要用于抢票,该程序需要从命令行传递参数,表示抢票人的名字。比如./buyticket allen 表示抢票人是 allen.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>

#define PERR(msg) do { perror(msg); exit(-1); } while(0)
#define PPERR(err, msg) do { err = errno; perror(msg); exit(-1); } while(0)

struct ticket {
  int remain;
  pthread_mutex_t lock;
};

int main(int argc, char* argv[]) {
  if (argc < 2) {
    printf("Usage: %s <name>\n", argv[0]);
    exit(-1);
  }

  char *name = argv[1];
  int err, shared, flag = 1;
  key_t key = 0x8888;
  int id = shmget(key, 0, 0); 
  if (id < 0) PERR("shmget");

  struct ticket *t = (struct ticket*)shmat(id, NULL, 0); 

  if ((int)t == -1) PERR("shmat");

  // 只要票数大于 0 就不断抢票。
  while(flag) {
    pthread_mutex_lock(&t->lock);
    int remain = t->remain;
    if (remain > 0) {
      sleep(1);
      printf("%s buy a ticket\n", name);
      --remain;
      sleep(3);
      t->remain = remain;
    }   
    else flag = 0;
    pthread_mutex_unlock(&t->lock);
    sleep(2);
  }

  err = shmdt((void*)t);
  if (err != 0) PERR("shmdt");

  return 0;
}

3.4 编译——Makefile 文件

此文件可以方便程序编译。

main:init destroy buyticket
init:init.c
  gcc init.c -o init -lpthread
destroy:destroy.c
  gcc destroy.c -o destroy -lpthread
buyticket:buyticket.c
  gcc buyticket.c -o buyticket -lpthread

注意,上面所有以 gcc 开头的行前面是个 tab 符号,即按下键盘上的 Tab 键所产生的,千万不能是空格。

完成些文件后,保存退出,然后在命令行键入

$ make

此时就会产生三个程序:init、destroy 和 buyticket

3.5 运行

  • 初始化
./init
  • 开启两个进程进行抢票

开启两个终端假设为终端 A 和终端 B. 在终端 A 中键入

./buyticket allen

终端 B 中键入

./buyticket luffy

记住这两个进程在执行时间上不能差的太远,别等到其中一个运行结束才开始另一个。

  • 运行结果


这里写图片描述
图1 运行结果

可以从图 1 中看到,一共有 5 张票,分别被两个不同进程中的线程所抢到,其中 allen 抢了 3 张票,而 luffy 抢了 2 张票。

完成了上面的程序的执行后,别忘记使用 ./destroy 对共享内存进行回收。

4. 总结

  • 掌握互斥量的共享属性
  • 掌握共享内存的使用

练习:上面的程序中,当 allen 抢到第 2 张票后,立即按下 CTRL + C 中断进程,会有什么后果?

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值