LINUX线程及线程间通信

线程概念

什么是线程

LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)
进程:独立地址空间,拥有PCB(进程控制块PCB(Process Control Block)
线程:也有PCB,但没有独立的地址空间(共享)
区别:在于是否共享地址空间。 独居(进程);合租(线程)。
Linux下: 线程:最小的执行单位,调度的基本单位。
进程:最小分配资源单位,可看成是只有一个线程的进程。
在linux下,线程最是小的执行单位;进程是最小的分配资源单位

线程共享资源

1.文件描述符表
2.每种信号的处理方式
3.当前工作目录
4.用户ID和组ID
5.内存地址空间 (.text/.data/.bss/heap/共享库)

线程非共享资源

1.线程id
2.处理器现场和栈指针(内核栈)
3.独立的栈空间(用户空间栈)
4.errno变量
5.信号屏蔽字
6.调度优先级

线程优、缺点

优点:
1、提高程序并发性
2、开销小
3、数据通信、共享数据方便
缺点:
1、库函数,不稳定
2、调试、编写困难、gdb不支持
3、对信号支持不好
优点相对突出,缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大。

线程相关操作函数

1
数据类型转换

1void *arg---->int i=0;
i=*( (int *)arg );
int i=1;---->void *arg
&i-->int * (&i)===>i;
pthread_create(&thread, NULL, start_routine, (void *)&i)
2int *ret[i];--->int 
ret[i]-->int *---->int *p中的p(地址)
*ret[i]----int
3int --->void *
int var;
pthread_exit(void *arg)
pthread_exit((void *)&var);

4int *ret[i];----void **类型
ret[i]--->int *类型
&ret[i]--int类型
    (void **)ret[i]---void **类型
    int pthread_join(pthread_t thread, void **retval);
pthread_join(tid[i], (void **)&ret[i])
//函数指针;void *(*start_routine) (void *) 
//指针函数: void *start_routine (void *arg)

创建线程程序例程1:

//错误判断--》等价于if(){perror(“”);return -1}
#define handle_error_en(en, msg) \
  do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
int *p=NULL;
//清理函数: 
void routine(void *arg)
{
  free(p);
  printf("this is routine free\n");
}
//子线程 
void *start_routine (void *arg)
{
  int i=0;
  static int stat=1;
  int oldstate=0;
  int oldtype=0;
  //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
  /*
  	PTHREAD_CANCEL_DISABLE	//不会被取消
  	在使用  PTHREAD_CANCEL_ENABLE前提下:pthread_setcanceltype(参数设置为:)
  	PTHREAD_CANCEL_DEFERRED//延時取消
      (该线程开始并不取消;等到下个取消点到来结束线程) 
 	PTHREAD_CANCEL_ASYNCHRONOUS//立即取消
  */
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);//可以取消
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);//立即取消
  
  //入栈 ---》清除资源成对出现
  pthread_cleanup_push(routine,NULL);
  p=(int *)malloc(sizeof(int));
  for(i=0;i<5;i++)
  {
    printf("this is my first pthread,the id is%lu\n",pthread_self());
    sleep(1);
  }
  free(p);
  printf("free success\n");
   //出栈 
  pthread_cleanup_pop(1);
  //退出子线程;返回子线程状态:stat
  pthread_exit((void *)&stat);
}
int main(int argc, char const* argv[])
{
  int i=0;
  pthread_attr_t attr;
  pthread_t thread=0;
  int num=10;
  void *stat=(void *)0;
  
  /*
   1.设置属性---》初始化 attr:线程属性 pthread_attr_init(&attr);

   2.设置线程分离属性 : int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
  	PTHREAD_CREATE_DETACHED:分离
	PTHREAD _CREATE_JOINABLE:非分离

  3.设置绑定属性 :int pthread_attr_setscope(pthread_attr_t *attr, int scope);
  PTHREAD_SCOPE_SYSTEM:绑定
  PTHREAD_SCOPE_PROCESS:非绑定 
  */
//分离,非绑定 
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
  pthread_attr_setscope(&attr,PTHREAD_SCOPE_PROCESS);
  //创建子线程 
  int ret=pthread_create(&thread,&attr,start_routine,(void *)&num);
  if(ret!=0)
  {
    handle_error_en(ret,"pthread_create");
  }
  for(i=0;i<5;i++)
  {
    printf("this is main thread,son id is %lu\n",thread);
    if(i==2)
    //终止线程 
      pthread_cancel(thread);
    sleep(1);
  }
  /*若使用下面语句--》运行报错--》核心已转储--》原因: pthread_exit((void *)&stat)还未将数值传入stsat就被终止----》pthread_join(thread,(void *)&stat);获取不到子线程状态*/

  //pthread_detach(thread);
  //pthread_join(thread,(void *)&stat);
  //printf("%d\n",*(int *)stat);
  pthread_join(thread,NULL);
  return 0;
}

创建线程程序例程2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 //var:全局变量----》子线程状态 
int var = 100;
//全局变量---》在子线程中开辟空间但未 释放 
int *p=NULL;
void routine(void *arg)
{
  free(p);
  printf("this is routine free\n");
}

 //创建的5个子线程 
void *tfn(void *arg)
{
	
	//入栈 
	pthread_cleanup_push(routine,NULL);
 	p=(int *)malloc(sizeof(int));
 	free(p);
 	//出栈 
  	pthread_cleanup_pop(1);
  	printf("free success\n");
	int i;
	i = (int)arg;
	sleep(i);
	//线程1: i为函数传参 
	if (i == 1) 
	{

		var = 1;
		printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i, pthread_self(), var);
		//返回线程1状态 ---同 pthread_exit((void *)var);
		return (void *)&var;
 
	}
	//线程3:
	else if (i == 3) 
	{
		printf("hello\n"); 
		var = 3;
		printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i, pthread_self(), var);
		//返回线程3状态
		pthread_exit((void *)&var);
	
 
	} 
	//	线程2/4/5 
	else  
	{
		printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i, pthread_self(), var);
		pthread_exit((void *)&var);
	}
 
	return NULL;
}
 //主线程 
int main(void)
{
	//创建多个线程----》数组保存多个线程ID 
	pthread_t tid[5];
	int i;
    //数组保存接受的多个线程状态:
	int *ret[5];  
 	//创建5个线程 第一个参数:pthread_t *thread; 第三个参数:子线程函数 
	for (i = 0; i < 5; i++)
		pthread_create(&tid[i], NULL, tfn, &i);
 	
	//分别获取子线程(0;1;2;3;4)状态 
	
	for (i = 0; i < 5; i++) 
	{
		if (i!=1)
		{
			//除线程2;其余进程全部解除与主进程关联 
			 pthread_detach(tid[i]);
			 //取消i==3(第四个进程) 
		     if (i==3)
			{
				pthread_cancel(tid[i]);	//自己添加取消点*/
			}
		}
		//第二个线程(i==1时进入else) 
		else 
		{
			 //阻塞父线程;等待子线程退出,获取线程退出状态
			pthread_join(tid[i], (void **)&ret[i]);
			//打印出子线程状态 
			printf("-------%d 's ret = %d\n", i, (int)ret[i]);	
		}
		
	
	}
        
	printf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), var);
 
	sleep(i);
	return 0;
}
 

线程私有数据 (不同线程对同一键值取不同的值)

线程私有数据 (Thread-specific Data,或称为 TSD):
线程私有数据采用一键多值的技术,即一个键对应多个数值。
通过键值来访问数据,看似对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。
线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值

线程私有数据相关API
相关操作:

下面接口所需头文件:
#include <pthread.h>
1)创建线程私有数据
2)注销线程私有数据
3)设置线程私有数据的关联
4)读取线程私有数据所关联的值

程序例程:

/*
程序运行结果
I'm 1th pthread, id = 139960459020032	 var = 1	 setspecific_value=111
FUN1
 第1个子线程返回给主线程状态为1

线程4读取私有数据为:0
I'm 4th pthread, id = 139960433841920	 var = 4	 setspecific_value=444
FUN1
 第4个子线程返回给主线程状态为4

*/

#include "stdio.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 //var:全局变量----》子线程状态 
int var = 100;
//创建私有数据 ----》线程创建: 
pthread_key_t key;
//线程1私有数据清理函数1: 
 void fun1 (void *arg)
 {
 	printf("FUN1\n");
 } 
 //线程2私有数据清理函数2: 
  void fun2 (void *arg)
 {
 	printf("FUN2\n");
 } 
 //创建的5个子线程 
void *tfn(void *arg)
{
	int i;
	i = (int)arg;
	//不同的睡眠时间--》运行结果不同??????
	sleep(i);
	//线程1: i为函数传参 
	if (i == 1) 
	{
		int value=111;
		var = 1;
		//2----设置线程私有数据的关联
		pthread_setspecific(key, &value);
		printf("I'm %dth pthread, id = %lu\t var = %d\t setspecific_value=%d\n", i, pthread_self(), var,*((int *)pthread_getspecific(key)));
		//返回线程1状态 ---同 pthread_exit((void *)&var);
		pthread_exit((void *)&var);
	}
	//线程3:
	else if (i == 3) 
	{
		var = 3;
		printf("I'm %dth pthread, pthread_id = %lu\t var = %d\n", i, pthread_self(), var);
		//返回线程3状态
		pthread_exit((void *)var);
	} 
	//线程4:读取线程私有数据所关联的值
	else if (i== 4) 
	{
		int value=444;
		var = 4;
		pthread_setspecific(key, &value);	
		printf("I'm %dth pthread, id = %lu\t var = %d\t setspecific_value=%d\n", i, pthread_self(), var,*((int *)pthread_getspecific(key)));
		//返回线程3状态
		pthread_exit((void *)var);
	}
	//	线程2/4/5 
	else  
	{
		sleep(1);
		printf("I'm %dth pthread, pthread_id = %lu\t var = %d\n", i, pthread_self(), var);
		pthread_exit((void *)var);
	}
	return NULL;
}


 **//主线程** 
int main(void)
{
	//创建多个线程----》数组保存多个线程ID 
	pthread_t tid[5];
	int i;
    //数组保存接受的多个线程状态:
	int *ret[5];  
//主函数------------------------------------------创建私有数据: 
       	pthread_key_create(&key, fun1);
 	//创建5个线程 第一个参数:pthread_t *thread; 第三个参数:子线程函数 
	for (i = 0; i < 5; i++)
		pthread_create(&tid[i], NULL, tfn, (void *)i);
 	
	//分别获取子线程(0;1;2;3;4)状态 
	
	for (i = 0; i < 5; i++) 
	{
		 //取消i==3(第四个进程) 
		     if ( (i==3)||(i==0) )
			{
				pthread_cancel(tid[i]);	//自己添加取消点*/
				sleep(1);
			}
		   if (i==2)
			{
				pthread_cancel(tid[i]);	//自己添加取消点*/
				sleep(1);
			}
		//第二个线程(i==1时进入else) 
		else if ( (i==1)||(i==4) )
		{
			 //阻塞父线程;等待子线程退出,获取线程退出状态
			pthread_join(tid[i], (void **)&ret[i]);
			//打印出子线程状态 
			printf(" 第%d个子线程返回给主线程状态为%d\n", i, (int)ret[i]);	
		}
	}
	sleep(i);
	return 0;
}
 

线程间的同步和互斥

互斥信号量(互斥锁)与信号量
1、为什么进行同步以及互斥:实现对资源的保护、独占

互斥锁的作用
保护共享数据: 在并发机制的情况下,有时候会有多个线程同时访问同一片数据,为了保护数据操作的准确性就需要通过加锁来进行保护。
保持操作互斥: 可能一个程序会有多个操作,但是同一个时间只能有一个操作被执行,例如a/b两个操作,如果a被执行,b就不能被执行,同理b被执行,a就不能执行操作函数
互斥锁分类
1、快速互斥锁
最常用的锁,符合以上的含义
2、检测锁
快速互斥锁的非阻塞版本
3、递归锁
多次加锁

**

互斥锁相关函数:

**

互斥锁相关操作函数:
1、pthread_mutex_t lock; /* 互斥锁定义 */
2pthread_mutex_init(&lock, NULL); /* 动态初始化,成功返回0,失败返回非0 */
3、pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化 */
4pthread_mutex_lock(&lock); /* 互斥锁上锁;阻塞的锁定互斥锁 
5、pthread_mutex_trylock(&thread_mutex);/* 互斥锁判断上锁;非阻塞的锁定互斥锁,成功获得互斥锁返回0,如果未能获得互斥锁,立即返回一个错误码 
6、pthread_mutex_unlock(&lock); /* 解锁互斥锁/
7、pthread_mutex_destroy(&lock) /* 销毁互斥锁 */

例程1: 互斥锁保护共享数据

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//全局变量:
static pthread_mutex_t g_mutex_lock;
static int g_count = 0;

static void *thread_fun_1(void *data)
{
   //加锁
    pthread_mutex_lock(&g_mutex_lock);

    g_count++;
    printf("%s g_count: %d\n", __func__, g_count);
    //解锁
    pthread_mutex_unlock(&g_mutex_lock);
}

static void *thread_fun_2(void *data)
{
    pthread_mutex_lock(&g_mutex_lock);

    g_count++;
    printf("%s g_count: %d\n", __func__, g_count);

    pthread_mutex_unlock(&g_mutex_lock);
}

static void *thread_fun_3(void *data)
{
    pthread_mutex_lock(&g_mutex_lock);

    g_count++;
    printf("%s g_count: %d\n", __func__, g_count);

    pthread_mutex_unlock(&g_mutex_lock);
}

int main(int argc, char const *argv[])
{
    int ret;
    pthread_t pid[3];
   //主线程初始化互斥锁
    ret = pthread_mutex_init(&g_mutex_lock, NULL);
    if (ret != 0) {
        printf("mutex init failed\n");
        return -1;
    }
    //创建3个子线程:
    pthread_create(&pid[0], NULL, thread_fun_1, NULL);
    pthread_create(&pid[1], NULL, thread_fun_2, NULL);
    pthread_create(&pid[2], NULL, thread_fun_3, NULL);
    //等待线程结束---阻塞主线程
    pthread_join(pid[0], NULL);
    pthread_join(pid[1], NULL);
    pthread_join(pid[2], NULL);
   //删除互斥锁:
    pthread_mutex_destroy(&g_mutex_lock);

    return 0;
}
/*
加互斥锁:
对数据g_count的操作进行加锁之后,同一个时间只有一个线程能获取到锁,也就是只有一个线程能对g_count进行操作,保证了g_count的数据的准确性打印结果:各进程依次运行
thread_fun_1 g_count: 1
thread_fun_2 g_count: 2
thread_fun_3 g_count: 3

若不加互斥锁:
3个线程都会对g_count进行操作,没有任何保护的情况下,3个线程是存在竞争关系的,所以g_count最终可能会是1、2、3三种值中的一种,

*/

实例2:保持操作的互斥性

有些情况下,2个不同的操作是不能同时进行的,例如fingerprint中的enroll和verify同一时间只能有一个操作进行。保持操作的互斥性本质上其实还是在保护共有的数据。下面的例子,打印hello的时候,world是无法打印的,如果希望打印world只能等待打印hello的线程退出之后再打印

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

static pthread_mutex_t g_mutex_lock;

static void *thread_fun_1(void *data)
{
  //阻塞锁:直到获取到锁才结束运行:阻塞其他线程
    pthread_mutex_lock(&g_mutex_lock);
    int i = 0;

    while (i < 5) {
        printf("hello\n");
        i++;
        sleep(1);
    }

    pthread_mutex_unlock(&g_mutex_lock);
}

static void *thread_fun_2(void *data)
{
    pthread_mutex_lock(&g_mutex_lock);
    int i = 0;

    while (i < 5) {
        printf("world\n");
        i++;
        sleep(1);
    }

    pthread_mutex_unlock(&g_mutex_lock);
}

static void do_print_hello()
{
    pthread_t pth_id;
    int result = pthread_create(&pth_id, NULL, thread_fun_1, NULL);
}

static void do_print_world()
{
    pthread_t pth_id;
    int result = pthread_create(&pth_id, NULL, thread_fun_2, NULL);
}

int main(int argc, char const *argv[])
{
    int ret;
    int cid;

    ret = pthread_mutex_init(&g_mutex_lock, NULL);
    if (ret != 0) {
        printf("mutex init failed\n");
        return -1;
    }
     printf("0---------------hello\n");
     printf("1---------------world\n");
     printf("2---------------EXIT\n");

    while (1) {
    	
    	printf("input a num: \n");
        scanf("%d", &cid);
        getchar();

        switch (cid) 
        {
            case 0:
                	do_print_hello();
            		break;
            case 1:
               	 	do_print_world();
            		break;
            case 2:
            		 pthread_mutex_destroy(&g_mutex_lock);
  			 return 0;
            default:	break;
        }
    }

}

非阻塞式的锁

上面的互斥锁是阻塞式的锁,也可以通过非阻塞式的锁进行,看下面的例子,pthread_mutex_trylock()函数如果获取到互斥锁了,会返回0,如果没有获取的互斥锁,会立即返回一个非0值,例子中通过g_cancel来通知线程进行退出,如果当前正在打印hello,发出打印world命令之后,通过pthread_mutex_trylock()就能知道当前有没有打印线程正在运行,如果有在运行的线程,通过置位g_cancel来退出正在运行的线程

/*
现象:
当按下打印hello命令时;打印hello;
再次按下打印hello命令会停止打印;按下打印world命令时打印world
world同Hello

原理:
定义全局变量:打印的标志位
针对于打印hello函数:
按下1命令------》开启线程1进入while中先对标志位进行检测;满足条件–》进行打印hello数据
再次按下1/0-----》又打开一个线程2;对标志位进行设置;—》导致线程1检测到标志改变打印停止
*/

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

static pthread_mutex_t g_mutex_lock;
//打印状态标志位:
static int g_cancel = 0;

static void *thread_fun_1(void *data)
{
printf("hello_id :%lu\n",pthread_self());
//非阻塞锁:检测不到锁直接返回0---》打印状态的全局变量值改变
    if (pthread_mutex_trylock(&g_mutex_lock) != 0) {
    	 printf("11111\n");
        g_cancel = 1;
        return 0;
    }

    int i = 0;
    g_cancel = 0;

while (i < 5) {
  //其他线程对全局变量打印状态的值改变时;该线程停止打印
        if (g_cancel) break;
        printf("hello\n");
        i++;
        sleep(1);
    }

    pthread_mutex_unlock(&g_mutex_lock);
}

static void *thread_fun_2(void *data)
{
    printf("world_id :%lu\n",pthread_self());
    if (pthread_mutex_trylock(&g_mutex_lock) != 0) {
    printf("2222\n");
        g_cancel = 1;
        return 0;
    }

    int i = 0;
    g_cancel = 0;
    
    while (i < 5) {
        if (g_cancel) break;
        printf("world\n");
        i++;
        sleep(1);
    }

    pthread_mutex_unlock(&g_mutex_lock);
}

static void do_print_hello()
{
    pthread_t pth_id;
    int result = pthread_create(&pth_id, NULL, thread_fun_1, NULL);
    
}

static void do_print_world()
{
    pthread_t pth_id;
    int result = pthread_create(&pth_id, NULL, thread_fun_2, NULL);
    
}

int main(int argc, char const *argv[])
{
    int ret;
    int cid;

    ret = pthread_mutex_init(&g_mutex_lock, NULL);
    if (ret != 0) {
        printf("mutex init failed\n");
        return -1;
    }
    printf("0---------------hello\n");
    printf("1---------------world\n");
    printf("2---------------EXIT\n");
    while (1) {
    	printf("input a num: \n");
        scanf("%d", &cid);
        getchar();

        switch (cid) {
            case 0:
                		do_print_hello();
            			break;
            case 1:
                		do_print_world();
           			 break;
	     case 2:
            		 	pthread_mutex_destroy(&g_mutex_lock);
  			 	return 0;
            default:
            			break;
        }
    }
}

Linux线程的信号量同步

信号量和互斥锁(mutex)的区别:
互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。
信号量相关操作函数:

头文件semaphore.h。
主要用到的函数:
·int sem_init(sem_t *sem, int pshared, unsigned int value);
·sem是要初始化的信号量,
·pshared表示此信号量是在进程间共享还是线程间共享
·value是信号量的初始值。
·int sem_destroy(sem_t *sem)  ----》销毁的信号量
·sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。
·int sem_wait(sem_t *sem);     -----》等待信号量,
·如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
 ·int sem_post(sem_t *sem); 释放信号量,
·让信号量的值加1。相当于V操作。

下列的代码演示了如何用信号量同步,模拟一个窗口服务系统。

//@purpose: 基于信号量的多线程同步,操作系统原理中的P,V操作
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define CUSTOMER_NUM 10
/* @Scene: 某行业营业厅同时只能服务两个顾客。
 * 有多个顾客到来,每个顾客如果发现服务窗口已满,就等待,
 * 如果有可用的服务窗口,就接受服务。 */
/* 将信号量定义为全局变量,方便多个线程共享 */
sem_t sem;
/* 每个线程要运行的例程 */
void * get_service(void *thread_id)
{
    /* 注意:立即保存thread_id的值,因为thread_id是对主线程中循环变量i的引用,它可能马上被修改 */
int customer_id = *((int *)thread_id);
//信号量大于0--》进行P操作(信号量-1操作)
    if(sem_wait(&sem) == 0)
     {
        //usleep(100);                /* service time: 100ms */
         sleep(1);
        printf("customer %d receive service ...\n", customer_id);
        //进行V操作(信号量+1)
        sem_post(&sem);
    }
}


int main(int argc, char *argv[])
{
    /* 初始化信号量,初始值为2,表示有两个顾客可以同时接收服务 */
    /* @prototype: int sem_init(sem_t *sem, int pshared, unsigned int value); */
    /* pshared: if pshared == 0, the semaphore is shared among threads of a process
     * otherwise the semaphore is shared between processes.   */
    sem_init(&sem, 0, 2);
    /* 为每个顾客定义一个线程id, pthread_t 其实是unsigned long int */
    pthread_t customers[CUSTOMER_NUM];
    int i, ret;
    /* 为每个顾客生成一个线程 */
    for(i = 0; i < CUSTOMER_NUM; i++)
    {
        int customer_id = i;
        ret = pthread_create(&customers[i], NULL, get_service, &customer_id);
        
        if(ret != 0)
        {
            perror("pthread_create");
            exit(1);
        }
        else
         {
            printf("Customer %d arrived.\n", i);
        }
        usleep(10);
    }

    /* 等待所有顾客的线程结束 */
    /* 注意:这地方不能再用i做循环变量,因为可能线程中正在访问i的值 */
    int j;
    for(j = 0; j < CUSTOMER_NUM; j++) 
    {
        pthread_join(customers[j], NULL);
    }
    /* Only a  semaphore that  has been initialized  by sem_init(3)
     * should be destroyed using sem_destroy().*/
    sem_destroy(&sem);
    return 0;
}

编译:gcc sem.c -lpthread

运行结果(注意,每次运行都不相同):

/*
实验现象:窗口每次只接受2人
Customer 0 arrived.
Customer 1 arrived.
Customer 2 arrived.
Customer 3 arrived.
Customer 4 arrived.
Customer 5 arrived.
Customer 6 arrived.
Customer 7 arrived.
Customer 8 arrived.
Customer 9 arrived.
customer 0 receive service ...
customer 1 receive service ...
customer 3 receive service ...
customer 2 receive service ...
customer 4 receive service ...
customer 5 receive service ...
customer 6 receive service ...
customer 7 receive service ...
customer 8 receive service ...
customer 9 receive service ...
*/

linux中的条件变量的使用

什么是条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制。
主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
条件变量类型为 pthread_cond_t。
条件变量有什么用
使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止。条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的。
如果条件为假,线程通常会基于条件变量阻塞,并以原子方式释放等待条件变化的互斥锁。如果另一个线程更改了条件,该线程可能会向相关的条件变量发出信号,从而使一个或多个等待的线程执行以下操作:

程序实例

条件变量的使用可以分为两部分:
等待线程
使用pthread_cond_wait前要先加锁;
pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活;
pthread_cond_wait被激活后会再自动加锁;
激活线程:
加锁(和等待线程用同一个锁);
pthread_cond_signal发送信号;
解锁;
激活线程的上面三个操作在运行时间上都在等待线程的pthread_cond_wait函数内部

/
程序执行结果:
decrement lock
incremen lock
进程2释放条件信号
进程1被唤醒
/

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//互斥锁变量定义
pthread_mutex_t count_lock;
//条件变量标志符定义
pthread_cond_t count_nonzero;
unsigned count = 0;
//子线程:
void *decrement_count(void *arg)
{
    //加锁
    pthread_mutex_lock(&count_lock);
    printf("decrement  lock\n");

    while(count == 0)
    {
        printf("decrement_count count == 0 \n");
        // 用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() 或 pthread_cond_broadcast来唤醒它 
        // pthread_cond_wait()通过(返回)时,该线程又自动获得该 mutex 。
        pthread_cond_wait(&count_nonzero, &count_lock);
        printf("进程1被唤醒 \n");
    }

    count = count + 1;
    pthread_mutex_unlock(&count_lock);
}

void *increment_count(void *arg)
{
    pthread_mutex_lock(&count_lock);
    printf("increment_count lock \n");
    if(count == 0)
    {
    	//发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.
    	//如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
        pthread_cond_signal(&count_nonzero);
        printf("进程2释放条件信号 \n");
    }

    count = count + 1;
    pthread_mutex_unlock(&count_lock);
}

int main(void)
{
    pthread_t tid1, tid2;
    //互斥锁与条件变量初始化:
    pthread_mutex_init(&count_lock, NULL);
    pthread_cond_init(&count_nonzero, NULL);
   //创建线程1:
    pthread_create(&tid1, NULL, decrement_count, NULL);
    sleep(2);
  //创建线程2:
    pthread_create(&tid2, NULL, increment_count, NULL);
    sleep(3);
   //等待线程结束
    pthread_exit(0);

    return 0;
}
  • TEACHER 遗留问题: 子线程等待父线程发送信号被唤醒—》成功 父线程等待子线程发送信号唤醒----》失败
#if 1

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define handle_error_en(en, msg) \
  do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
//线程私有数据
pthread_key_t key;
//线程互斥锁
pthread_mutex_t  mutex;
//线程条件变量
pthread_cond_t cond;
	//
void destr_function (void *arg)
{
			printf("this is fun2\n");
}
//子线程
void *starts_routine (void *arg)
{
			int i=0,num=20,std=0;
			static int stat=0,err=1;
			//设置私有数据
			pthread_setspecific(key,&num);
			
			//子线程上锁
			pthread_mutex_lock(&mutex);
			#if 1
			//条件变量无条件等待
			pthread_cond_wait(&cond,&mutex);
			printf("进程1被唤醒 \n");
			#endif
			
			for(i=0;i<5;i++)
			{
				//读取私有数据
				std=*(int *)pthread_getspecific(key);
				printf("this is my second pthread,the std is %d\n",std);
				sleep(1);
			}
			//解锁
			pthread_mutex_unlock(&mutex);
			//等待子线程退出;返回子线程状态:stat
			pthread_exit((void *)&stat);
}
int main(int argc, char const* argv[])
{
			int i=0;
			int num=10;
			void *stat=(void *)0;
			pthread_t thread=0,thread1=0;
	    		//互斥锁初始化
			pthread_mutex_init(&mutex,0);
			//条件变量初始化
			pthread_cond_init(&cond, NULL);
			//创建线程私有数据;清理函数:destr_function
			pthread_key_create(&key, destr_function);
			//创建子线程starts_routine;传入参数:num
			int ret1=pthread_create(&thread1,NULL,starts_routine,(void *)&num);
			if(ret1!=0)
			{
				handle_error_en(ret1,"pthread_create");
			}
			sleep(1);
			//主线程上锁
			pthread_mutex_lock(&mutex);
			printf("主进程被唤醒 \n");
			for(i=0;i<5;i++)
			{
				
				printf("this is main thread,son id is %lu\n",thread);
				if(i==2)
				{
					//唤醒一个等待条件的线程
					pthread_cond_signal(&cond);
					printf("主線程释放条件信号 \n");
				}	
			}
			//主线程开锁
			pthread_mutex_unlock(&mutex);
			//等待子线程结束
			pthread_join(thread,NULL);
			pthread_join(thread1,NULL);
			//删除私有数据
			pthread_key_delete(key);
			return 0;
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值