23-线程信号量

线程信号量
(1)进程信号量与线程信号量
  线程的信号量与进程的信号量几乎完全相同,只不过一个是给进程用的,另一个是给线程用的。
  使用进程信号量时,我们自己往往还需要二次封装,线程的信号量函数则不需要,直接就可以使用,所以线程的信号量使用起来更加容易,应该说使用难度非常低。
(2)二值信号量和多值信号量
  对于线程信号量来说,也分为二值信号量和多值信号量,同样的我们这里只讲二值信号量。
  使用二值信号量时,往往用来实现“互斥”和“同步”。
  如果想实现互斥的话,更多的还是使用前面讲的互斥锁来实现,因为线程互斥锁提供了更多可自供选择的功能,比如可以设置为“检错锁”、“递归锁”等。
  如果你只是想实现简单互斥的话,不管是使用线程互斥锁的“快锁”来实现,还是使用线程信号量来实现,最终所实现的互斥效果都是一样的
(3)信号量的使用步骤
  1)定义信号量集合
  (a)用于互斥时,集合中只需要一个信号量。
  (b)用于同步时,有几个线程需要同步,集合中就需要包含几个信号量

  2)初始化集合中的每个信号量
    设置初始值,二值信号量的初始值要么是0、要么是1。
    (a)如果是用于互斥,基本都是设置为1
    (b)如果是用于同步,看具体情况
      和进程信号量时的情况是一样的。

  3)p、v操作
  p操作:信号量值-1
  V操作:信号量值+1

  4)进程结束时,删除线程信号量集合

(4)线程信号量相关的函数

  1)初始化信号量的函数

  (a)函数原型
  #include <semaphore.h>

  int sem_init(sem_t *sem, int pshared, unsigned int value);
·   功能:初始化线程信号量集合中的某个信号量,给它设置一个初始值。

  · 返回值
  成功返回0,失败返回-1,errno被设置。
  注意信号量的错误号不是返回的,而是设置到errno中。

  · 参数
  - sem:信号量集合中的某个信号量

  信号量集合需要我们自己定义
  比如:sem_t sem[3],
  线程信号量集合其实就是一个数组,数组每个元素就是一个信号量。

  sem[0]:第一个信号量
  sem[1]:第二个信号量
  sem[2]:第三个信号量

  sem_init(&sem[0], int pshared, unsigned int value);

  线程信号量集合其实就是自定义的一个数组,而进程信号量集合则是通过semget函数创建的。

  我们只要把数组定义为全局变量,所有的线程即可共享使用,不像进程信号量,需要semid才能实现共享操作。

  - pshared:
  + 0:给线程使用
  + !0:可以给进程使用
  不过对于进程来说,我们更多的还是使用进程信号量,因为线程信号量用到进程上时,存在一些不稳定的情况。

  - value:初始化值
  对于二值信号量来说,要么是1,要么是0

  · 使用信号量实现互斥,让多个线程向同一文件写“hello” “world\n”时不要出
  现hello hello world的情况。

  实现互斥时,信号量集合中只需要一个信号量,而且初始值需要被设置为1。
  sem_t sem[1]。//定义信号量集合,集合只有一个元素

  sem_init(&sem[0], 0, 1);

  for(i=0;i<1;i++)
  {
    if(i == 0) sem_init(&sem[i], 0, 1);
    else sem_init(&sem[i], 0, 0);
  }
3)P操作
(a)函数原型
  #include <semaphore.h>
  int sem_wait(sem_t *sem);//阻塞p操作

  · 功能:阻塞p操作集合中某个信号量,值-1
  如果能够p操作成功最好,否则就阻塞直到p操作操作成功为止

  · 返回值:成功返回0,失败返回-1,errno被设置。

  · 参数:p操作的某个信号量。
  比如:sem_wait(&sem[0]);

  · sem_wait的兄弟函数

  int sem_trywait(sem_t *sem):不阻塞
  如果能够p操作就p操作,如果不能p操作就出错返回,不会阻塞。
  int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  可以设置阻塞时间,如果能够p操作就p操作,不能就阻塞,如果在设置的时间内好没有
  p操作成功就是出错返回,不再阻塞

4)v操作
  (a)函数原型
    #include <semaphore.h>

    int sem_post(sem_t *sem);

    · 功能:对某个信号量进行v操作,v操作不存在阻塞问题。
    v操作成功后,信号量的值会+1

    · 返回值:成功返回0,失败返回-1,errno被设置。

  (b)代码演示
    sem_post(&sem[0]);
  2)删除信号量
  (a)函数原型
    #include <semaphore.h>
    int sem_destroy(sem_t *sem);

  · 功能:删除某个信号量,把所有信号量都删除后,信号量集合就被销毁。

  这与删除进程信号量集合有所不同,对于进程信号量集合来说,只要删除一个信号量,整个集合
  即被删除,但是对于线程信号量来说,需要一个一个的删除,当所有信号量都删除完后,集合才被
  删除完毕

(b)代码pth_sem_mutex.c

  

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <semaphore.h>
#include <unistd.h>


#define SECON_PTH_NUMS 2

#define PTHEXIT -1
#define SEM_NUMS 1 //信号量数量


typedef struct pthread_arg{
    pthread_t tid;
    int pthno;
    int fd;
}ptharg;

//全局变量定义后,默认初始化为0
struct global_va{
    ptharg pth_arg[SECON_PTH_NUMS]; //结构体数组,每个元素会被当做参数传递给对应的次线程
    sem_t sem[1];

} glbva;

void print_err(char *str,int line,int err_no){
    printf("%d,%s:%s\n",line,str,strerror(err_no) );
    exit(-1);
}
void signal_fun(int signo){
        int ret =0;
        ret = sem_destroy(&glbva.sem[0]);
        if(ret != 0) print_err("sem_destory err",__LINE__,errno);
        exit(0);
}

void *pth_fun(void *pth_arg){
    int fd= ((ptharg *)pth_arg)->fd;
    
    while(1){
        sem_wait(&glbva.sem[0]);
        write(fd,"php",3);
        write(fd,"java\n",5);
        sem_post(&glbva.sem[0]);
    }
    return NULL;
}



int main(int argc, char const *argv[])
{
    
    int i=0;
    int ret =0;
    int fd =0;
    signal(SIGINT,signal_fun);

    fd=open("./file",O_RDWR|O_CREAT|O_TRUNC,0664);
    if(fd ==-1) print_err("open file fail",__LINE__,errno);

    //初始化信号量
    ret = sem_init(&glbva.sem[0],0,1);
    if(ret != 0) print_err("open sem_init err",__LINE__,errno);

    for(i=0;i<SECON_PTH_NUMS;i++){
        glbva.pth_arg[i].fd =fd;
        glbva.pth_arg[i].pthno=i;
        ret= pthread_create(&glbva.pth_arg[i].tid,NULL,pth_fun,(void *)&glbva.pth_arg[i]);
        if(ret !=0 ) print_err("pthread_creat err",__LINE__,ret);
        

    }

    while(1){
        sem_wait(&glbva.sem[0]);
        write(fd,"js",2);
        write(fd,"css\n",4);
        sem_post(&glbva.sem[0]);
    }
    return 0;
}

 


(5)使用线程信号量,实现线程之间的同步
  比如有三个线程(1主线程,2个次线程),分别打印333333、222222、111111,使用同步让他们顺序
  的打印111111、222222、333333。

· 使用信号量实现同步

  使用进程信号量实现进程同步时,有多少个进程需要同步,集合中就需要包含几个信号量。

  同样的,使用线程信号量实现同步时,有几个线程需要同步,集合中就需要包含几个信号量

  pth_sem_sync.c

 

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <semaphore.h>
#include <unistd.h>

#define SECON_PTH_NUMS 2

#define PTHEXIT -1
#define SEM_NUMS SECON_PTH_NUMS+1 //信号量数量,主线程占用一个


typedef struct pthread_arg{
    pthread_t tid;
    int pthno;
    int fd;
}ptharg;

//全局变量定义后,默认初始化为0
struct global_va{
    ptharg pth_arg[SECON_PTH_NUMS]; //结构体数组,每个元素会被当做参数传递给对应的次线程
    sem_t sem[SEM_NUMS];

} glbva;

void print_err(char *str,int line,int err_no){
    printf("%d,%s:%s\n",line,str,strerror(err_no) );
    exit(-1);
}
void signal_fun(int signo){
        int i= 0;
        int ret =0;
        for (i=0;i<SEM_NUMS;i++){
            ret = sem_destroy(&glbva.sem[i]);
            if(ret != 0) print_err("sem_destory err",__LINE__,errno);

        }
        exit(0);
    
}

void *pth_fun1(void *pth_arg){
    while(1){
        sem_wait(&glbva.sem[0]);
        printf("111111\n");
        sleep(1);
        sem_post(&glbva.sem[1]);
    }
    return NULL;
}
void *pth_fun2(void *pth_arg){
    while(1){
        sem_wait(&glbva.sem[1]);
        printf("222222\n");
        sleep(1);
        sem_post(&glbva.sem[2]);
    }
    return NULL;
}

int main(int argc, char const *argv[])
{
    
    int i=0;
    int ret =0;
    void *(*pth_fun_buf[])(void *)={pth_fun1,pth_fun2};


    signal(SIGINT,signal_fun);

    #if 1

    //初始化信号量

    for(i=0;i<SEM_NUMS;i++){
        if(i == 0) ret = sem_init(&glbva.sem[i],0,1);
        else ret = sem_init(&glbva.sem[i],0,0);
        if(ret != 0) print_err("open sem_init err",__LINE__,errno);
    }
    #endif

    for(i=0;i<SECON_PTH_NUMS;i++){
        ret= pthread_create(&glbva.pth_arg[i].tid,NULL,pth_fun_buf[i],NULL);
        if(ret !=0 ) print_err("pthread_creat err",__LINE__,ret);
        

    }

    while(1){
        sem_wait(&glbva.sem[2]);
        printf("33333\n");
        sleep(1);
        sem_post(&glbva.sem[0]);
        
    }
    return 0;
}

 

posted @ 2018-10-21 13:17  H&K  阅读(369)  评论(0)    收藏  举报