linux多线程同步之互斥锁、信号量、条件量
更新:HHH   时间:2023-1-7


  目录

  一.线程同步之信号量

  1、任务:用户从终端输入任意字符然后统计个数显示,输入end则结束

  2、信号量的介绍和使用(多线程实行的引入)

  (1)、信号量初始化

  (2)、等待信号量

  (3)、释放信号量

  (4)、销毁信号量

  3、使用多线程实现:主线程获取用户输入并判断是否退出,子线程计数

  二.线程同步之互斥锁

  1、什么是互斥锁

  (1)互斥锁又叫互斥量(mutex)

  (2)相关函数:

  2、用互斥锁来实现上节的代码,

  三.线程同步之条件变量

  1、什么是条件变量

  2、相关函数

  3、使用条件变量来实现上节代码

  一.线程同步之信号量

  1、任务:用户从终端输入任意字符然后统计个数显示,输入end则结束

  int main(void)

  {

  char buf[100] = {0};

  printf("请输入一个字符串,以回车结束\n");

  while(scanf("%s",buf))

  {

  //去比较输入的是否为end,是则退出,不是则继续

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序结束\n");

  return 0;

  }

  printf("本次输入了%d个字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));

  }

  return 0;

  }

  2、信号量的介绍和使用(多线程实行的引入)

  与进程一样,线程也可以使用信号量来通信。线程使用信号量同步线程的步骤如下:

  (1)、信号量初始化

  int sem_init (sem_t *sem , int pshared, unsigned int value);

  对sem指定的信号量进行初始化,pshared:设置好共享选项(linux只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。

  (2)、等待信号量

  int sem_wait(sem_t *sem);

  给信号量减1,然后等待直到信号量的值大于0。

  (3)、释放信号量

  int sem_post(sem_t *sem);

  信号量值加1。并通知其他等待线程。

  (4)、销毁信号量

  int sem_destroy(sem_t *sem);

  3、使用多线程实现:主线程获取用户输入并判断是否退出,子线程计数

  E:\Linux\3.AppNet\6.thread\6.3

  char buf[100] = {0};

  sem_t sem;

  unsigned int flag=0;

  void *func(void *arg)//子线程计数

  {

  //子线程首先应该有个循环

  /*循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,然后打印,完成后再次阻塞*/

  while(flag==0)

  {

  //②等待信号量,信号量没来之前一直阻塞在这里

  sem_wait(&sem);

  printf("本次输入了%d个字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));//清除buf

  sem_wait(&sem);

  }

  pthread_exit(NULL);//线程终止

  }

  int main(void)

  {

  pthread_t th = -1;

  int ret = -1;

  //①信号量初始化

  sem_init (&sem , 0, 0);

  /* 创建线程 */

  ret = pthread_create(&th, NULL, func, NULL);

  if(ret !=0)

  {

  perror("pthread_create error.\n");

  return -1;

  }

  printf("请输入一个字符串,以回车结束\n");

  while(scanf("%s",buf))

  {

  //去比较输入的是否为end,是则退出,不是则继续

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序结束\n");

  //return 0;

  flag=1;//标志量为1.则结束

  //③释放信号量

  sem_post(&sem);

  break;

  }

  /*主线程在收到用户收入的字符串,并且确认不是end后,就去发信号激活子 进程来计数*/

  //子线程被阻塞,主线程可以激活,这就是线程的同步问题。

  //信号量就可以用来实现线程同步

  sem_post(&sem);//继续时也释放信号量

  }

  //回收子线程

  printf("等待回收子线程\n");

  ret=pthread_join(th, NULL );

  if(ret!=0)

  {

  perror("pthread_join error.\n");

  return -1;

  }

  printf("子线程回收成功\n");

  //④销毁信号量

  sem_destroy(&sem);//销毁信号量

  return 0;

  }

  二.线程同步之互斥锁

  1、什么是互斥锁

  (1)互斥锁又叫互斥量(mutex)

  作用:

  互斥锁用来保证同一时间内只有一个线程在执行某段代码(临界区)。

  互斥锁可看作某种意义上的全局变量。在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经被上锁的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。互斥锁保证让每个线程对共享资源按顺序进行原子操作。

  (2)相关函数:

  互斥锁初始化: pthread_mutex_init()

  互斥锁上锁: pthread_mutex_lock()

  互斥锁判断上锁:pthread_mutex_trylock()

  互斥锁解锁: pthread_mutex_unlock()

  消除互斥锁: pthread_mutex_destroy()

  初始化互斥锁

  int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr);

  mp是互斥锁地址

  mattr是属性,通常默认 null 。初始化互斥锁之前,必须将其所在的内存清零。如果互斥锁已初始化,则它会处于未锁定状态。互斥锁可以位于进程之间共享的内存中或者某个进程的专用内存中。

  锁定互斥锁

  int pthread_mutex_lock(pthread_mutex_t *mutex);

  当pthread_mutex_lock() 返回时,该互斥锁已被锁定。调用线程是该互斥锁的属主。如果该互斥锁已被另一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可用为止。

  返回值:pthread_mutex_lock() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。

  EAGAIN:由于已超出了互斥锁递归锁定的最大次数,因此无法获取该互斥锁。

  EDEADLK:当前线程已经拥有互斥锁。

  解除锁定互斥锁

  int pthread_mutex_unlock(pthread_mutex_t *mutex);

  函数说明:pthread_mutex_unlock() 可释放 mutex 引用的互斥锁对象。

  返回值:pthread_mutex_unlock() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

  EPERM :当前线程不拥有互斥锁。

  销毁互斥锁

  int pthread_mutex_destroy(pthread_mutex_t *mp);

  注意,没有释放用来存储互斥锁的空间。

  返回值:pthread_mutex_destroy() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。

  EINVAL: mp 指定的值不会引用已初始化的互斥锁对象。

  2、用互斥锁来实现上节的代码,

  用多线程实现:主线程获取用户输入并判断是否退出,子线程计数

  E:\Linux\3.AppNet\6.thread\6.4

  char buf[100] = {0};

  pthread_mutex_t mutex;

  unsigned int flag=0;

  void *func(void *arg)//子线程计数

  {

  //子线程首先应该有个循环

  /*循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,然后打印,完成后再次阻塞*/

  sleep(1);//先睡眠1s,让出CPU给主线程,保证主线程先运行

  while(flag==0)

  {

  //②互斥锁上锁:

  pthread_mutex_lock(&mutex);

  printf("本次输入了%d个字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));//清除buf

  //③互斥锁解锁:

  pthread_mutex_unlock(&mutex);

  sleep(1);//再次睡眠一下,把上锁动作交给主线程

  }

  pthread_exit(NULL);//线程终止

  }

  int main(void)

  {

  pthread_t th = -1;

  int ret = -1;

  //①初始化互斥锁

  pthread_mutex_init(&mutex,NULL);

  /* 创建线程 */

  ret = pthread_create(&th, NULL, func, NULL);

  if(ret !=0)

  {

  perror("pthread_create error.\n");

  return -1;

  }

  //主线程

  printf("请输入一个字符串,以回车结束\n");

  while(1)

  {

  //②互斥锁上锁:

  pthread_mutex_lock(&mutex);

  scanf("%s",buf);

  //③互斥锁解锁:

  pthread_mutex_unlock(&mutex);

  //去比较输入的是否为end,是则退出,不是则继续

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序结束\n");

  //return 0;

  flag=1;//标志量为1.则结束

  break;

  }

  sleep(1);//再次睡眠一下,把上锁动作交给子线程

  }

  //回收子线程

  printf("等待回收子线程\n");

  ret=pthread_join(th, NULL );

  if(ret!=0)

  {

  perror("pthread_join error.\n");

  return -1;

  }

  printf("子线程回收成功\n");

  //④销毁互斥锁

  pthread_mutex_destroy(&mutex);

  return 0;

  }

  三.线程同步之条件变量

  1、什么是条件变量

  与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。

  通常条件变量和互斥锁同时使用。条件变量分 为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局 变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在 互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步

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

  2、相关函数

  初始化条件变量

  phread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

  等待条件成立

  释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)

  int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

  激活条件变量

  int pthread_cond_signal(pthread_cond_t *cond);

  int pthread_cond_broadcast(pthread_cond_t *cond); 解除所有线程的阻塞

  清除条件变量

  无线程等待,否则返回EBUSY

  int pthread_cond_destroy(pthread_cond_t *cond);

  3、使用条件变量来实现上节代码

  char buf[100] = {0};

  pthread_mutex_t mutex;

  pthread_cond_t cond;

  unsigned int flag=0;

  void *func(void *arg)//子线程计数

  {

  //子线程首先应该有个循环,郑州 不  孕 不  育 医 院:http://wapyyk.39.net/zz3/zonghe/1d427.html/

  /*循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,然后打印,完成后再次阻塞*/

  //sleep(1);//先睡眠1s,让出CPU给主线程,保证主线程先运行

  //这里的sleep也用不着了,因为下面的pthread_cond_wait这会阻塞

  while(flag==0)

  {

  //①互斥锁上锁:

  pthread_mutex_lock(&mutex);

  //Ⅱ等待条件成立,即等待主线程激活条件变量

  pthread_cond_wait(&cond,&mutex);

  //再执行下面printf的计数。

  printf("本次输入了%d个字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));//清除buf

  //②互斥锁解锁:

  pthread_mutex_unlock(&mutex);

  //sleep(1);//再次睡眠一下,把上锁动作交给主线程

  }

  pthread_exit(NULL);//线程终止

  }

  int main(void)

  {

  pthread_t th = -1;

  int ret = -1;

  //初始化互斥锁

  pthread_mutex_init(&mutex,NULL);

  //Ⅰ初始化条件变量

  pthread_cond_init(&cond,NULL);

  /* 创建线程 */

  ret = pthread_create(&th, NULL, func, NULL);

  if(ret !=0)

  {

  perror("pthread_create error.\n");

  return -1;

  }

  printf("请输入一个字符串,以回车结束\n");

  while(1)

  {

  //①互斥锁上锁:这里就没必要上锁了。

  //pthread_mutex_lock(&mutex);

  scanf("%s",buf);

  //Ⅲ激活条件变量,唤醒正被此条件变量阻塞的子线程

  pthread_cond_signal(&cond);

  //②互斥锁解锁:这里y也没必要解锁了 包括下面的sleep

  //pthread_mutex_unlock(&mutex);

  //去比较输入的是否为end,是则退出,不是则继续

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序结束\n");

  //return 0;

  flag=1;//标志量为1.则结束

  break;

  }

  //sleep(1);//再次睡眠一下,把上锁动作交给子线程

  }

  //回收子线程

  printf("等待回收子线程\n");

  ret=pthread_join(th, NULL );

  if(ret!=0)

  {

  perror("pthread_join error.\n");

  return -1;

  }

  printf("子线程回收成功\n");

  //销毁互斥锁

  pthread_mutex_destroy(&mutex);

  //Ⅳ清除条件变量

  pthread_cond_destroy(&cond);

  return 0;

  }


返回开发技术教程...