`
shinepaopao
  • 浏览: 145060 次
社区版块
存档分类
最新评论

Linux操作系统中多线程的同步

阅读更多

1 互斥锁

  互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。

  先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。

  void reader_function ( void );

  void writer_function ( void );

  char buffer;

  int buffer_has_item=0;

  pthread_mutex_t mutex;

  struct timespec delay;

  void main ( void ){

  pthread_t reader;

  /* 定义延迟时间*/

  delay.tv_sec = 2;

  delay.tv_nec = 0;

  /* 用默认属性初始化一个互斥锁对象*/

  pthread_mutex_init (&mutex,NULL);

  pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);

  writer_function( );

  }

  void writer_function (void){

  while(1){

  /* 锁定互斥锁*/

  pthread_mutex_lock (&mutex);

  if (buffer_has_item==0){

  buffer=make_new_item( );

  buffer_has_item=1;

  }

  /* 打开互斥锁*/

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

  }

  }

  void reader_function(void){

  while(1){

  pthread_mutex_lock(&mutex);

  if(buffer_has_item==1){

  consume_item(buffer);

  buffer_has_item=0;

  }

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

  }

  }

  这里声明了互斥锁变量mutex,结构pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。在上面的例子中,使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上锁、解锁机制,一般情况下,选用最后一个默认属性。

  pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。在上面的例子中,使用了pthread_delay_np函数,让线程睡眠一段时间,就是为了防止一个线程始终占据此函数。

  在使用互斥锁的过程中很有可能会出现死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁2,a线程先锁定互斥锁1,b线程先锁定互斥锁2,这时就出现了死锁。此时可以使用函数pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点。

  2 条件变量

  前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。

  条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:

  extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));

  其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结构,和互斥锁一样可以用它来设置条件变量是进程内可用还是进程间可用,默认值是PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为pthread_cond_ destroy(pthread_cond_t cond)。

  函数pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:

  extern int pthread_cond_wait __P ((pthread_cond_t *__cond,

  pthread_mutex_t *__mutex));

  线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等,这一点从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。

  另一个用来阻塞线程的函数是pthread_cond_timedwait(),它的原型为:

  extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,

  pthread_mutex_t *__mutex, __const struct timespec *__abstime));

  它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也被解除。

  函数pthread_cond_signal()的原型为:

  extern int pthread_cond_signal __P ((pthread_cond_t *__cond));

  它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号有可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。下面是使用函数pthread_cond_wait()和函数pthread_cond_signal()的一个简单的例子。

  pthread_mutex_t count_lock;

  pthread_cond_t count_nonzero;

  unsigned count;

  decrement_count () {

  pthread_mutex_lock (&count_lock);

  while(count==0)

  pthread_cond_wait( &count_nonzero, &count_lock);

  count=count -1;

  pthread_mutex_unlock (&count_lock);

  }

  increment_count(){

  pthread_mutex_lock(&count_lock);

  if(count==0)

  pthread_cond_signal(&count_nonzero);

  count=count+1;

  pthread_mutex_unlock(&count_lock);

  }

  count值为0时,decrement函数在pthread_cond_wait处被阻塞,并打开互斥锁count_lock。此时,当调用到函数increment_count时,pthread_cond_signal()函数改变条件变量,告知decrement_count()停止阻塞。

  函数pthread_cond_broadcast(pthread_cond_t *cond)用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。

  3 信号量

  信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。下面逐个介绍和信号量有关的一些函数,它们都在头文件/usr/include/semaphore.h中定义。

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

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

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

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

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

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

  下面来看一个使用信号量的例子。在这个例子中,一共有4个线程,其中两个线程负责从文件读取数据到公共的缓冲区,另两个线程从缓冲区读取数据作不同的处理(加和乘运算)。

  /* File sem.c */

  #include

  #include

  #include

  #define MAXSTACK 100

  int stack[MAXSTACK][2];

  int size=0;

  sem_t sem;

  /* 从文件1.dat读取数据,每读一次,信号量加一*/

  void ReadData1(void){

  FILE *fp=fopen("1.dat","r");

  while(!feof(fp)){

  fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);

  sem_post(&sem);

  ++size;

  }

  fclose(fp);

  }

  /*从文件2.dat读取数据*/

  void ReadData2(void){

  FILE *fp=fopen("2.dat","r");

  while(!feof(fp)){

  fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);

  sem_post(&sem);

  ++size;

  }

  fclose(fp);

  }

  /*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/

  void HandleData1(void){

  while(1){

  sem_wait(&sem);

  printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],

  stack[size][0]+stack[size][1]);

2
4
分享到:
评论

相关推荐

    linux上实现多进程和多线程实现同步互斥(源代码)

    在Linux操作系统中,多进程和多线程是两种并发执行的方式,它们在处理并发问题时,经常需要进行同步和互斥操作,以确保数据的一致性和程序的正确性。本篇将详细介绍这两种并发模型以及如何在Linux环境中实现同步互斥...

    Linux系统下的多线程编程入门.pdf

    本文将深入探讨Linux环境中的多线程概念、创建与管理线程的方法、线程同步与通信机制,以及多线程编程中可能遇到的问题和解决策略。 一、多线程概念 多线程是指在一个进程中可以同时执行多个独立的代码段,每个代码...

    操作系统实验报告——线程与进程同步

    实验内容集中在Linux下的多线程同步机制上,具体通过修改生产者-消费者问题的示例程序来实现。在这个问题中,多个生产者线程生成数据,而消费者线程负责消费这些数据。为了保证数据的一致性和正确性,需要防止多个...

    操作系统上机-Linux多线程编程

    在操作系统的学习过程中,掌握Linux多线程编程是至关重要的技能之一。Linux作为一款广泛使用的开源操作系统,其强大的系统调用接口和丰富的库函数为开发者提供了实现多线程应用的便利条件。多线程编程允许程序同时...

    简单实现多线程同步示例(模拟购票系统)

    本示例“简单实现多线程同步示例(模拟购票系统)”旨在通过一个具体的实例,帮助开发者理解如何在Java中创建并管理多线程以及如何实现线程同步,确保数据的一致性和正确性。 首先,我们要明确多线程的基本概念。在...

    Linux系统编程之线程同步

    所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成...

    Linux操作系统下的多线程编程详细解析.doc

    Linux 操作系统下的多线程编程详细解析 Linux 操作系统下的多线程编程是指在 Linux 操作系统下使用多线程技术来提高系统性能和效率。多线程技术可以使系统更加 response、更加并发、更加高效。 多线程技术的优点有...

    Linux操作系统编程(linux多线程的互斥与同步控制及实践)

    在深入讨论Linux操作系统编程,特别是多线程的互斥与同步控制及实践之前,首先需要了解Linux操作系统的基本概念以及多线程编程的基础知识。Linux是一种类Unix的操作系统,它支持多用户、多任务、多线程和多种硬件...

    linux 操作系统 多线程编程 经典同步算法--读者写者问题

    因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。

    linux操作系统之多线程.pdf

    在Linux操作系统中,多线程是一种并发执行任务的机制,允许程序同时处理多个任务或子任务,从而提高系统的效率和响应速度。这份实验报告详细介绍了如何在Linux环境下使用C语言和POSIX线程库(pthread)来实现一个多...

    重大软院操作系统实验三:线程同步

    在这个实验中,我们将深入探讨线程同步的基本概念、方法以及在Linux操作系统中的实现。 线程同步是指在多线程环境中,控制多个线程对共享资源的访问,确保它们按照一定的顺序或者规则执行,以防止数据不一致性和竞...

    linux系统下多线程编程.PDF

    Linux系统下的多线程编程是指在Linux操作系统中,使用C语言等编程工具和多线程技术来编写能够同时执行多个任务的程序。多线程编程相较于传统的单线程编程,可以让程序更加高效地使用计算机的CPU资源,提高程序的响应...

    嵌入式Linux应用程序开发第9章多线程编程

    本章主要讲解了 Linux 中的多线程编程,包括 Linux 线程的基本概念、创建及使用、属性设置、同步与互斥问题的处理等内容。通过学习本章,读者将掌握 Linux 中线程的基本概念、线程的创建和使用、线程属性的设置、多...

    分析linux操作系统之多线程.pdf

    在本实验报告中,主题是Linux操作系统的多线程实现,通过编写一个简单的单词统计工具来探讨这一概念。多线程是一种并发执行程序的方式,它允许多个任务在同一时间片段内并行运行,从而提高系统效率和响应速度。在...

    多线程编程和操作系统线程同步互斥演示

    在这个“多线程编程和操作系统线程同步互斥演示”中,作者可能创建了一个或多个人工场景,展示了如何在VC++环境中使用多线程,并且演示了线程同步和互斥的实践应用。这可能涉及到以下几个方面: 1. **线程创建**:...

    操作系统实验(linux),多线程管理,多进程管理

    在本实验中,我们将深入探讨Linux操作系统中的两个关键概念:多线程管理和多进程管理。这些概念对于理解和优化系统的性能至关重要,特别是在并发编程和系统资源调度方面。 首先,让我们了解多线程管理。在计算机...

    linux多定时器多线程

    在Linux操作系统中,多线程和多定时器是两种强大的技术,它们被广泛应用于各种复杂的系统和服务中。本文将深入探讨如何在Linux环境下利用多线程和多定时器来实现不同任务的并发执行,以及它们如何协同工作以提高程序...

Global site tag (gtag.js) - Google Analytics