linux多线程----信号量

来源:互联网 发布:云海软件 编辑:程序博客网 时间:2024/05/23 19:14

信号量:信号量是操作系统中所用到的PV原子操作,他广泛用于进程与线程的同步与互斥,信号量本质是一个非负的整数计数器,被用来控制对公共资源的访问,下面是pv操作的工作原理:

     PV原子操作是对整数计数器信号量sem的操作,一次p操作使sem减1,一次v操作使sem加1,进程或者线程根据信号量判断是否对公共资源具有访问权限,当信号量sem的值大于等于0时,则进程或线程具有公共资源的访问权,当信号量小于0时,则进程或线程会被阻塞,直到信号量sem大于等于0为止

信号值的含义:

     如果信号量的值大于0表示可用的资源数,小于0表示阻塞的进程数

p操作:

     将信号量减一后,如果>=0,则表示有可用的资源,线程继续执行

信号量减一后,如果<0,则线程会被阻塞,放在阻塞队列中

v操作:

     将信号量加一后,如果大于0表示没有阻塞的线程

     将信号量加一后,如果小于0或者等于0,说明之前s<0表示有线程在等待队列中,从等待队列中取出一个线程执行。

pv操作必须成对的使用

pv操作主要用于进程或线程的同步和互斥两种情况,如果用于互斥,只需设置一个信号量sem,其流程图如下:

当信号量用于同步操作时,通常需要设置多个信号量,并安排不同的初始值来实现他们的之间的执行顺序,其流程如下:


linux实现了POSIX的无名信号量,主要用于线程间的互斥与同步,这里主要介绍了几个函数:

  • sem_init()用于创建一个信号量,并初始化他的值
  • sem_wait()和sem_trywait()都相当于P操作,当信号量大于0时他们都会将信号量减一,两者的区别是当信号量小于0时,sem_wait()会阻塞进程,而sem_trywait()会立即返回
  • sem_post()相当于V操作,他将信号量的值加1同时发出信号来唤醒等待的进程
  • sem_getvalue()用于得到信号量的值
  • sem_destroy()用于删除信号量

sem_init()函数格式如下:

sem_wait()函数语法如下:

下面这个例子通过信号量实现三个线程有序的执行,按照创创建的相反顺序执行:


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_NUMBER 3//线程个数
#define REPEAT_NUMBER 3
#define DELAY_TIME_LEVELS 10.0
void *thrd_func(void *arg)//线程函数
{
    int thrd_num = (int)arg ;
    int delay_time = 0 ;
    int count = 0 ;
    sem_wait(&sem[thrd_num]) ;//执行P操作,信号量的值减一
    printf("thread %d is starting\n", thrd_num) ;
    for (count  = 0; count < REPEAT_NUMBER; count++)//每个线程完成五次任务
    {
        delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;//产生随机时间
        sleep(delay_time) ;
        printf("\tThread %d:job %d delay = %d\n",
                    thrd_num, count, delay_time) ;
    }
    printf("thread %d finished\n", thrd_num) ;
    pthread_exit(NULL) ;
}
int main()
{
    pthread_t thread[THREAD_NUMBER] ;
    int no = 0, res ;
    void *thrd_ret ;
    srand(time(NULL)) ;//产生随机种子
    pthread_mutex_init(&mutex, NULL ) ;//初始化互斥锁,默认的是快速互斥锁
    for(no = 0; no < THREAD_NUMBER; no++)
    {
              sem_init(&sem[no], 0, 0) ;//初始化信号量,将其值设为0
        res = pthread_create(&thread[no], NULL, thrd_func, (void *)no) ;//创建线程
        if (res != 0)
        {
            printf("create thread %d failed\n", no) ;
            exit(res) ;
        }
    }
    printf("create threads success\n waiting for threads to finish...\n") ;
    sem_post(&sem[THREAD_NUMBER - 1]) ;//对最后一个信号量执行V操作

    for (no = THREAD_NUMBER - 1; no >= 0; no--)
    {
        res = pthread_join(thread[no], &thrd_ret) ;
        if (!res)
        {
            printf("thread %d joined\n", no) ;
        }
        else
        {
            printf("thread %d joined failed", no) ;
        }
        sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]) ;//对相应的信号量执行V操作
    }
          for (no = 0; no < THREAD_NUMBER; no++)
    {
        sem_destroy(&sem[no]) ;//销毁信号量
    }
    exit(0) ;
}


其原理就是利用pv操作来进行线程的控制,首先三个线程都被阻塞住了,然后对第三个信号量执行V操作,依次向上,这样线程就从创建的反方向执行了,其结果如下:

可以明显看出信号量控制了线程的执行顺序。



0 0
原创粉丝点击