信号量sem_t,互斥锁pthread_mutex_t的使用

来源:互联网 发布:sql清除表中所有数据 编辑:程序博客网 时间:2024/05/19 04:05

信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:  

extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));  T>

sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。  T>

函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。  T>

函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。  T>

函数sem_destroy(sem_t *sem)用来释放信号量sem。 T>

信号量用sem_init函数创建的,下面是它的说明:
  #include<semaphore.h>
        int sem_init (sem_t *sem, int pshared, unsigned int value);

        这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。我们现在只对不让进程共享的信号量感兴趣。 (这个参数受版本影响), pshared传递一个非零将会使函数调用失败。

  这两个函数控制着信号量的值,它们的定义如下所示:
  
  #include <semaphore.h>
        int sem_wait(sem_t * sem);
        int sem_post(sem_t * sem);
 
        这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
        sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。
        sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,介信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
         信号量这种“只用一个函数就能原子化地测试和设置”的能力下正是它的价值所在。还有另外一个信号量函数sem_trywait,它是sem_wait的非阻塞搭档。

         最后一个信号量函数是sem_destroy。这个函数的作用是在我们用完信号量对它进行清理。下面的定义:
          #include<semaphore.h>
          int sem_destroy (sem_t *sem);
          这个函数也使用一个信号量指针做参数,归还自己战胜的一切资源。在清理信号量的时候如果还有线程在等待它,用户就会收到一个错误。
         与其它的函数一样,这些函数在成功时都返回“0”。

摘自:百度百科:http://baike.baidu.com/view/1461738.htm

1. 创建和销毁

  有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下: pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

  动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

  pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

  2. 互斥锁属性

  互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

  * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

  * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

  * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

  * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

  3. 锁操作

  锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

  int pthread_mutex_lock(pthread_mutex_t *mutex)

  int pthread_mutex_unlock(pthread_mutex_t *mutex)

  int pthread_mutex_trylock(pthread_mutex_t *mutex)

  pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

 

/*==============================================================================
 bufsem.c
 功能:主进程负责向队列(就2个元素)写入数据,创建的两个线程负责读取数据。
 同步互斥:主进程和线程之间用信号量sem_t进行同步,线程之间依靠互斥锁进行互斥操作。
 问题:进程和线程都使用了信号量,可不可以免去互斥锁?

注意:直接编译gcc -o bufsem bufsem.c会出现如下错误

undefined reference to `sem_init'
undefined reference to `sem_post'
undefined reference to `sem_wait'

编译选项需要加入一个多线程

gcc -pthread -o bufsem bufsem.c

*/==============================================================================

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

#define N 2 //队列中元素的数量

struct _buf_
{
 char *buf[N];
 int front, rear; //有from和rear,显然是个队列
}BUF;

void * thread_function_r(void *arg); //线程函数
/*
信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:  
extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));  
__sem为指向信号量结构的一个指针;__pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;__value给出了信号量的初始值。  
*/
sem_t sem_r, sem_w; //semphore,信号量,本程序用于保证进程和线程间的同步

pthread_mutex_t mutex; //互斥锁,保证线程访问的互斥,步骤一,定义数据类型

int main(int argc, char *argv[])
{

 int res;
 pthread_t a_thread_r, b_thread_r; //线程ID变量,与进程类似,进程为pid_t
 
 for (res=0; res<N; res++)  BUF.buf[res] = (char*) malloc (30); //为buf指针数组分配空间
 BUF.rear = BUF.front = 0;
 /*信号量用sem_init函数创建的,下面是它的说明:
  #include<semaphore.h>
        int sem_init (sem_t *sem, int pshared, unsigned int value);
    这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。
 */
 res = sem_init(&sem_r, 0, 0);
 if ( res != 0) 
 {
  perror(strerror(errno));
  exit(EXIT_FAILURE);
 }
 res = sem_init(&sem_w, 0, 2);
 if ( res != 0) 
 {
  perror(strerror(errno));
  exit(EXIT_FAILURE);
 }
 
 if ( pthread_mutex_init(&mutex, NULL) != 0 ) //互斥锁,保证线程访问的互斥,步骤二,初始化,默认属性初始化互斥量——NULL参数的功能
 {
  perror(strerror(errno));
  exit(EXIT_FAILURE);
 }
 /*
 #include<pthread.h>
  int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void*(*start_rtn)(void*),void *restrict arg);
  返回值:若成功则返回0,否则返回出错编号
  返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
 */
 res = pthread_create(&a_thread_r, NULL, thread_function_r, NULL); //创建读取线程一
 if ( res != 0 )
 {
  perror(strerror(errno));
  exit(EXIT_FAILURE);
 }
 res = pthread_create(&b_thread_r, NULL, thread_function_r, NULL); //创建读取线程二
 if ( res != 0 )
 {
  perror(strerror(errno));
  exit(EXIT_FAILURE);
 }
 
 while(1) //【功能】主进程执行写入操作
 {
   sem_wait(&sem_w); //函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。函数sem_destroy(sem_t *sem)用来释放信号量sem。
   fputs("Input any words, its words must less than 30: ", stdout);
   fgets(BUF.buf[BUF.rear], 30, stdin);
   if (strcmp("end\n", BUF.buf[(BUF.front) % N]) == 0 ) break;
   BUF.rear = (BUF.rear + 1) % N;
   sem_post(&sem_r); //函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。
   usleep(1);
 }

 return 0;
}
 
void * thread_function_r(void *arg) //【功能】线程负责读取
{
 while(1)
 {
  sem_wait(&sem_r);
  pthread_mutex_lock(&mutex); //获取互斥量(互斥锁),另外有pthread_mutex_trylock尝试对互斥量加锁,如果失败返回EBUSY
  fprintf(stdout, "Print:BUF.buf[%d] by %u: %s", BUF.front,pthread_self(), BUF.buf[BUF.front]);
  BUF.front = (BUF.front + 1) % N;
  pthread_mutex_unlock(&mutex); //释放互斥锁
  sem_post(&sem_w);
 }
}
来自http://canlynet.blog.163.com/blog/static/25501365200911208257812/ 感谢作者

原创粉丝点击