天天看点

Linux线程浅析[线程的同步和互斥之线程信号量]

Linux线程浅析[线程的同步和互斥之线程信号量]

  1. 什么是线程信号量
  2. 线程信号量的相关函数
  3. 使用信号量来进行互斥和同步的问题

什么是线程信号量

 在之前有一篇博客讲了进程的信号量,singal函数,这个函数是用来捕获系统中某些信号的,而在线程中同样是有这样的一个信号两,这个信号量就是用来处理线程的同步互斥问题的.

 信号量的本质是一个非负整数计数器,是共享资源的数目,通常被用来控制对共享资源的访问信号量可以实现线程的同步和互斥,通过sem_post()和sem_wait()函数对信号量 进行加减操作从而解决线程的同步和互斥

信号量数据类型: sem_t

线程信号量的相关函数

信号量数据类型:sem_t

#include<semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned value);
int sem_destory(sem_t *sem);
返回:成功返回0,出错返回错误编号
参数:
    sem信号量指针
    pthread是否在进程间共享的标志,0为不共享,1为共享
    value初始的时候信号量值
           

信号量的加减操作

#include<semaphore.h>
int sem_post(sem_t *sem);
    功能:增加信号量的值
int sem_wait(sem_t *sem);
    功能:减少信号量的值
int sem_trywait(sem_t *sem);
    功能:sem_wait()的非阻塞版本
    返回:0成功,出错返回错误编码
           

 注意:

调用:sem_post()一次信号量作加1操作

调用sem_wait()一次信号量减1操作

当线程调用sem_wait后,若信号量的值小于0,则线程阻塞,只有其它线程在调用sem_post对信号量做加操作后并且其值大于或者等于0的时候,阻塞的线程才能继续运行;

一般调用原则:

如果当做互斥锁的时候,一般初始化的时候初始化为1,然后对其进行PV操作

如果需要进行同步的时候,一般初始化的时候信号量为0,然后对其进行PV操作

最简单的同步函数

/*
 * ===========================================================================
 *
 *       Filename:  pthread_sem_mutex.c
 *    Description:
 *    使用线程信号量来控制其执行的顺序,让执行顺序分别为one,two,three
 *        Version:  1.0
 *        Created:  2017年04月04日 20时41分25秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>

//定义两个全局变量,sem_one,sem_two
sem_t sem_one,sem_two;

/* *
 *线程1执行的函数,由于其第一个执行,所以是不需要进行等待的,但是其需要唤醒别的
 所以在执行结束后需要post去将计数器加1
 * */
void *one_th(void* argv){
  printf("one:%lx runing\n",pthread_self());
  sem_post(&sem_one);
  return (void*);
}

/* *
 *线程2执行的函数
 * */
void *two_th(void* argv){
  sem_wait(&sem_one);

  printf("two:%lx runing\n",pthread_self());
  sem_post(&sem_two);
  return (void*);
}

/* *
 *线程3执行的函数
 * */
void *three_th(void* argv){
  sem_wait(&sem_two);
  printf("three:%lx runing\n",pthread_self());
  return (void*);
}


int main(int argc,char * argv[]){
  pthread_t thread_one,thread_two,thread_three;

  //初始化函数,因为是同步,所以将其初始化的时候设置为0
  sem_init(&sem_one,,);
  sem_init(&sem_two,,);
  int err =  ;

  if((err = pthread_create(&thread_one,NULL,one_th,(void*))) != ){
      perror("one thread create error");
  }

  if((err = pthread_create(&thread_two,NULL,two_th,(void*))) != ){
      perror("one thread create error");
  }

  if((err = pthread_create(&thread_three,NULL,three_th,(void*))) != ){
      perror("one thread create error");
  }


  pthread_join(thread_one,NULL);
  pthread_join(thread_two,NULL);
  pthread_join(thread_three,NULL);
  //信号量的销毁函数
  sem_destroy(&sem_one);
  sem_destroy(&sem_two);

  return ;
}
           

输出的结果为:

Linux线程浅析[线程的同步和互斥之线程信号量]

如何使用信号量来实现线程的互斥呢???

wait的时候是信号减1,post是加1,如果要互斥的话,那么必定只能有一个线程能去操纵共享资源.所以第一个进去的必定是执行的,有两种方法,1:将信号的初始值设置为1,或者进去先post,但是如果先post的话,也就意味着每个线程都可以去执行.所以只能初始化的时候设置为1,然后在执行的时候,将信号量做-1操作

对之前的银行账户相关的案例进行一种改写:

/*
 * ===========================================================================
 *
 *       Filename:  account.h
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月28日 22时04分18秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#ifndef __ACCOUNT_H_
#define __ACCOUNT_H_
//声明银行账户的结构体
//引用pthread头文件.方便后面加锁
#include<pthread.h>
#include<semaphore.h>
  typedef struct{
    int code;
    double balance;
    //将锁声明在结构体中,有助于每一个这样的结构体都有一把锁,那么在操作的时候,有利于优化并发效果
    //pthread_mutex_t pthreadmutex;
    //pthread_rwlock_t pthreadrwlock;
    //
    //初始化信号量
    sem_t sem_lock;
  }Account;

//创建银行账户
extern Account* create_account(int code,double balance);

//销毁一个账户
extern void destory_account(Account* account);

//存款
extern double withdraw(Account* account,double amt);

//取款
extern double deposit(Account* a,double amt);

//查看账户余额
extern double get_balance(Account* account);

#endif
           
/*
 * ===========================================================================
 *
 *       Filename:  account.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月30日 21时16分28秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>
#include<semaphore.h>
//static pthread_mutexattr_t attr;
/* 
 *创建一个账户
 *返回这个账户的指针
 *
 * */
extern Account* create_account(int code,double balance){
    Account* account = (Account*)malloc(sizeof(Account));
    assert(account !=NULL);
    account->code = code;
    account->balance = balance;
    //初始化锁的过程
    //pthread_mutexattr_init(&attr);
    //pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);
    //pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
    //pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK);
    //pthread_mutex_init(&account->pthreadmutex,NULL);
    //pthread_mutex_init(&account->pthreadmutex,&attr);

    //初始化读写锁
    // pthread_rwlock_init(&account->pthreadrwlock,NULL);

    // 初始化信号量
    sem_init(&account->sem_lock,,);

    return account;
}
/* *
 *销毁一个账户
 * */
extern void destory_account(Account* account){
   assert(account!=NULL);
   free(account);
   //销毁锁的过程
   //pthread_mutexattr_destroy(&attr);
   //pthread_mutex_destroy(&account->pthreadmutex);
   //
   //销毁读写锁
   //pthread_rwlock_destroy(&account->pthreadrwlock);
   sem_destroy(&account->sem_lock);
}

/* *
 *取款,返回的是账户的余额
 *
 * */
extern double withdraw(Account * account,double amt){
  assert(account != NULL);
  //上锁的过程
  //pthread_mutex_lock(&account->pthreadmutex);
  //上读写锁(读锁)
  //pthread_rwlock_wrlock(&account->pthreadrwlock);

  sem_wait(&account->sem_lock);

  if(amt <  || amt > account->balance){
    //解锁的过程
    //pthread_mutex_unlock(&account->pthreadmutex);
    //解读写锁
    //pthread_rwlock_unlock(&account->pthreadrwlock);
    //信号量post
    sem_post(&account->sem_lock);
    return ;
  }
  int balance = account -> balance;
  sleep();
  int result = balance - amt;
  account ->balance = result;
  //解锁的过程
  //pthread_mutex_unlock(&account->pthreadmutex);
  //
  //解读写锁
  //pthread_rwlock_unlock(&account->pthreadrwlock);
  //信号量post
  sem_post(&account->sem_lock);

  return amt;
}
/* *
 *存款,返回的也是账户的余额
 *
 * */
extern double deposit(Account* account,double amt){
   assert(account != NULL);
   //上锁的过程
   //pthread_mutex_lock(&account->pthreadmutex);
  //上读写锁(读锁)
  //pthread_rwlock_wrlock(&account->pthreadrwlock);
  //信号量wait
  sem_wait(&account->sem_lock);
   if(amt < ){
     //解锁的过程
    //pthread_mutex_unlock(&account->pthreadmutex);
    //解读写锁
    //pthread_rwlock_unlock(&account->pthreadrwlock);
    //信号量post
    sem_post(&account->sem_lock);
      return ;
   }
   int balance = account ->balance;
   sleep();
   int result = balance + amt;
   account->balance = result;
   //解锁的过程
   //pthread_mutex_unlock(&account->pthreadmutex);
    //解读写锁
    //pthread_rwlock_unlock(&account->pthreadrwlock);

   sem_post(&account->sem_lock);

   return result;
}

/* *
 *获取的就是这个账户的余额
 *
 * */
extern double get_balance(Account* account){
  assert(account !=NULL);
  //上读写锁(读锁)
  // pthread_rwlock_rdlock(&account->pthreadrwlock);

  sem_wait(&account->sem_lock);
  int result = account->balance;
  sem_post(&account->sem_lock);

  //解决读写锁
  //pthread_rwlock_unlock(&account->pthreadrwlock);
  return result;
}
           

这些就是简单的关于线程信号量相关的使用过程,注意在作为同步的时候使用的话,那么将初始计数器设置为0,而作为互斥锁来使用的话,那么将初始计数器的数值设置为1.

欢迎持续访问博客

继续阅读