zoukankan      html  css  js  c++  java
  • 信号量、互斥锁和条件变量的区别

    一、

    1、互斥锁总是必须由给其上锁的线程解锁,信号量的挂出确不必由执行过它的等待操作的同一线程执行。

                                    生产者与消费者伪代码

    2、互斥锁要么被锁住,要么被解锁(二值状态,类似于二值信号量)

    3、既然信号量有一个与之关联的状态(它的数值),那么信号量的挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么信号将丢失。

    ps:提供信号量的原因是,在进程间同步的情况下,若没有涉及到共享内存区时,需要使用信号量。

    二、

      1、posix提供量中信号量:有名信号量和基于内存的信号量,后者被称为无名信号量。

      

    有名信号量如下图所示:

      

    内存信号量(无名信号量)如下图所示:

        

    三、有名信号量

      sem_open:创建一个新的有名信号量或者打开一个已经存在的有名信号量。有名信号量既可用于线程间同步,也可用于进程间同步。

    头文件:#include <semaphore.h>
    函数原型:sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value);
    参数:
    name 信号量的外部名字
    oflag 选择创建或打开一个现有的信号量
    mode 权限位
    value 信号量初始值
    oflag参数可以是0、O_CREAT(创建一个信号量)或O_CREAT|O_EXCL(如果没有指定的信号量就创建),如果指定了O_CREAT,那么第三个和第四个参数是需要的;其中mode参数指定权限位,value参数指定信号量的初始值,通常用来指定共享资源的书面。该初始不能超过SEM_VALUE_MAX,这个常值必须低于为32767。二值信号量的初始值通常为1,计数信号量的初始值则往往大于1。
    
    
    如果指定了O_CREAT(而没有指定O_EXCL),那么只有所需的信号量尚未存在时才初始化它。所需信号量已存在条件下指定O_CREAT不是一个错误。该标志的意思仅仅是“如果所需信号量尚未存在,那就创建并初始化它”。但是所需信号量等已存在条件下指定O_CREAT|O_EXCL却是一个错误。
    
    
    sem_open返回指向sem_t信号量的指针,该结构里记录着当前共享资源的数目。
     

    sem_close用于关闭打开着的信号量:

    #include <semaphore.h>
    
    int sem_close(sem_t *sem)

    sem_unlink将有名信号量从系统中删除

    #include <semaphore.h>
    
    int sem_unlink(const char *name)
    sem_wait是一个函数,也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。
    如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_trywait(sem_t *sem)是函数sem_wait的非阻塞版,它直接将信号量sem减1,同时返回错误代码EAGAIN。
    #include <semaphore.h>
    
    int sem_wait(sem_t *sem);
    int sem_trywait(sem_t *sem);

    sem_post是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;

    #include <semaphore.h>
    int sem_post(sem_t *sem);

    sem_getvalue,计算机术语,是把 sem 指向的信号量当前值放置在 sval 指向的整数上。 如果有一个或多个进程或线程当前正在使用 sem_wait(3) 等待信号量,POSIX.1-2001 允许返回两种结果在 sval 里:要么返回 0;要么返回一个负值,它的绝对等于当前正在 sem_wait(3) 里阻塞的进程和线程数。Linux 选择了前面的行为(返回零)。

    #include <semaphore.h>
    
    int sem_getvalue(sem_t *sem,int *sval);

    posix有名信号量至少是随内核持续性的,因此可以跨多个进程操作他们。

     四、无名信号量

     之前的内容处理的是posix有名信号量的内容,这些信号量是由一个name参数标识的,它通常指代文件系统中的某个文件。然而posix也提供了基于内存的信号量。

    int sem_init(sem_t *sem, int pshared, unsigned int value);
    如果 pshared 是0,那么初始化的信号量是在同一个进程的各个线程间共享的。
    如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 
    创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。
    初始化一个已经初始的信号量其结果未定义。 参数 sem :指向信号量对象   pshared : 指明信号量的类型。不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享。   value : 指定信号量值的大小 返回值 sem_init() 成功时返回
    0;错误时,返回 -1,并把 errno 设置为合适的值。 错误 EINVAL value 超过 SEM_VALUE_MAX。 ENOSYS pshared 非零,但系统还没有支持进程共享的信号量。

    摧毁信号量:

    #include <semaphore.h>
    
    int sem_destroy(sem_t *sem);

      进程间共享基于内存信号量的规则:信号量本身(其作为sem_init第一个参数的sem_t数据类型变量)必须驻留在由所有希望共享它的进程所共享的内存区中,而且sem_init的第二个参数必须是1。

      在父进程中打开的任何信号量仍应在子进程中打开。如下:

      

    sem_t *sem;
    
    sem = sem_open("sem name",O_CREATE|O_EXCL,FILE_MODE,0);
    if(childpid = fork() == 0)
    {
            sem_wait(sem);      
    }
    else
    {
            sem_post(sem);  
    }

      

  • 相关阅读:
    使用replaceAll替换“/”为“/”
    如何在Lucene里面进行数字范围搜索 (Numeric Range Query)
    Java中的参数传递
    安装配置WordPress 3.0的多站点功能
    Apache Hadoop 项目介绍
    Java中的Set操作
    使用Desktop API in Java SE 6打开文件或者网页
    Java集合(转帖)
    数据结构牛客网习题纠错130
    PAT 1045 Favorite Color Stripe[dp][难]
  • 原文地址:https://www.cnblogs.com/rabbit0212/p/11231653.html
Copyright © 2011-2022 走看看