linux系统调用线程

1. 基础概念

早期unix系统中,没有线程概念,后来才引入线程
 linxu 为了迎合 windows引入了线程 ,linux 上进程是非常优秀了,linux 上用线程和进程的区别不大,老程序都是用进程
 gdb不支持线程,因为gdb比线程出现了早
 区别:
 线程: 有独立的pcb.  没有独立的进程地址空间,线程在进程内部,共享进程地址空间
 进程: 有独立的进程 地址空间,有独立的pcb
进程是分配资源的最小单位,如何理解: *  如果一个进程A有3个线程,那么cpu会把A进程当作4个进程,cpu分配时间片个A进程    给优先
 是不是线程越多进程优先级越高: 但线程数量达到一定程度的时候,优先级会降低
  进程是分配资源的最小单位,  线程是最小的执行单位

fork() 原理, 底层调用clone() 克隆一份, 70% 的的东西从父进程中复制过来,
自己改一下 ID ,

线程共享 进程的数据 , 多个线程都指向同一个页目录

pcb 三级页表:   多个线程  指向 页表, mmu  , 最终指向的物理内存 地址是相同的, 所以多个线程共享进程资源

线程是最小执行单位,进程最小分配空间单位 , cpu 调度

4. 

LWP号: 线程的号 ,cpu根据线程号分配轮片   火狐浏览器就是用多线程开发的 ,使用线程池机制
线程id:  进程中区分不通过线程的

ps -Lf pid : 查看进程下有哪些线程

05_线程优缺点和共享资源

优点:
1.  线程共享:
2. 文件描述符表
3. 信号处理方式
4. 当前工作目录
5. 用户id 和组 ID
6. 内存空间地址

缺点:
非共享: 线程ID  , 栈 ,  栈 (每个线程独立栈)、 errno变量 、 信号屏蔽字、

线程优点:
  提供程序并发:一个进程有多个线程, 可以多争取cpu
  开销下, 每一个进程都要开辟0-4G虚拟空间,线程不要
缺点:
  线程锁函数都是第三方库函数, 稳定性不好,
  不支持gdb
  读信号不支持不好,信号首先出来,线程后面出来

建议: 用线程了就别用进程了,否则负责度很大

  本章总结 线程进程函数总结对比
  *    pthread_create()   fork()
  *    pthread_self()     getpid()
  *    pthread_exit()     exit()
  *    pthread_join()     wait/waitpid
  *    pthread_cancle()   kill
  *    pthread_detach()

 2. 线程函数 

   2.1.pthread_create

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>


void* tfun(void* args){
	int* j=(int *)args;
	printf("son thread.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),*j);
   // printf("---%d---",*j);
	return NULL;
}
int main0000014(){
    /**
     *  线程共享资源:
     *  1. 文件描述符表
     *  2. 当前工作目录
     *  3. 用户ID 和组 ID
     *  4. 内存地址空间(.text/.data/.bss/堆/共享库)[共享全局变量]
     *  非共享:
     *1. 线程id
     *2. 栈
     *  优点: 效率高
     */

	// lwp 标识线程身份,cpu调度区分   线程id:  进程内部不同线程区分
	pthread_t tid;
	// 线程id,线程属性,回调函数,回调函数参数
	// 打印主线程
	printf("main thread..pid = %d ... pthread %lu \n",getpid(),pthread_self());
    int j=1000;
	int ret = pthread_create(&tid,NULL,tfun,&j);  // 返回线程id
	sleep(1);  //子线程还没有输出,父进程已经死了,避免
	if(ret == -1){
		perror("pthread_create fail");
		exit(-1);
	}
	// gcc single.c -o single.o -lpthread
     // 第三方库要加 -lpthread
	return 0;
}

   2.1.1线程传递问题

void* tfun(void* args){
	int* j=(int *)args;
	printf("son thread.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),*j);
   // printf("---%d---",*j);
	return NULL;
}

/**
 * 循环创建多个线程
 */
int main0000015() {
	pthread_t tid;
	int ret=0;
	for (int i = 0; i < 10; i++) {
		// 错误
		/**
		 * son thread.... pid = 3521 ...  140422606616320-args--2
son thread.... pid = 3521 ...  140422589830912-args--5
son thread.... pid = 3521 ...  140422598223616-args--3
son thread.... pid = 3521 ...  140422615009024-args--1
son thread.... pid = 3521 ...  140422581438208-args--7
son thread.... pid = 3521 ...  140422564652800-args--7
son thread.... pid = 3521 ...  140422573045504-args--7
son thread.... pid = 3521 ...  140422556260096-args--8
son thread.... pid = 3521 ...  140422479345408-args--9
son thread.... pid = 3521 ...  140422470952704-args--10
 出现重复数据,
 为什么: for循环不断执行当,但i=5的时候在栈中,子线程回调才第一次执行,才切换成内核态取出i=5,实际的需要的是0
 解决方法: 直接传递值
		 */
		ret= pthread_create(&tid, NULL, tfun, (void*) &i);  // 返回线程id
		if (ret !=0 ) {
			perror("pthread_create fail");
			exit(-1);
		}
	}
	sleep(1);
	return 0;
}

   2.2.pthread_self

void* tfun1(void* args){
	int j=(int )args;
	printf("son thread...tfun1.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),j);
   // printf("---%d---",*j);
	return NULL;
}
int main0000016() {
	pthread_t tid;
	int ret=0;
	for (int i = 0; i < 10; i++) {
		ret= pthread_create(&tid, NULL, tfun1, (void*) i);  // 返回线程id
		if (ret !=0 ) {
			perror("pthread_create fail");
			exit(-1);
		}
	}
	sleep(1);
	return 0;
}

2.3.pthred_exit

/**
 *  exit: 退出进程
 *  pthred_exit 退出线程
 */
void* tfun3(void* args){
	int j=(int )args;
	if(j==2){
	//	return NULL;  // 返回到函数调用者那里去 , 退出以后不会往下走了
		pthread_exit(NULL);
	}
	printf("son thread...tfun1.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),j);
   // printf("---%d---",*j);
	return NULL;
}
int main0000017() {
	pthread_t tid;
	int ret=0;
	for (int i = 0; i < 10; i++) {
		ret= pthread_create(&tid, NULL, tfun3, (void*) i);  // 返回线程id
		if (ret == -1) {
			perror("pthread_create fail");
			exit(-1);
		}
	}
	sleep(1);

	return 0;
}

2.4.pthread_join 阻塞回收子线程 相当有 wait , 首先把自己执行完毕在回收

/**
 * pthread_join 阻塞回收子线程 相当有 wait
 */
void* tfun4(void* args){
	return (void*)1100;
}
int main0000018() {
	pthread_t tid;
	//  成功返回0
    int ret= pthread_create(&tid, NULL, tfun4, NULL);  // 返回线程id
    if(ret !=0){
    	perror("pthread_create erro");
    	exit(-1);
    }
    int *retval;
    // 参数类型void**
    // 参数1 线程id  参数2 回调函数返回值 tfun4
 // 回收正确返回0 
    ret = pthread_join(tid,(void**)&retval);
    if(ret!=0){
     	perror("pthread_create erro");
        exit(-1);
    }
    // 返回值类型 -- return (void*)1100;
     printf("resutl---%d",retval);
	return 0;
}

设计指针的时候处理方式1:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>


typedef struct 
{
  char ch;
  int var;
  char str[64];
}exit_t;

/**
 * pthread_join 阻塞回收子线程 相当有 wait
 */
void* tfun4(void* args){
  exit_t* retvar = (exit_t*) malloc(sizeof(exit_t));
  retvar->ch='m';
  retvar->var=3000;
  strcpy(retvar->str,"my thread\n");

  pthread_exit((void*)retvar);
}



int main() {
  pthread_t tid;
  //  成功返回0
    int ret= pthread_create(&tid, NULL, tfun4, NULL);  // 返回线程id
    if(ret !=0){
      perror("pthread_create erro");
      exit(-1);
    }


    exit_t *retval;
    // 参数类型void**
    // 参数1 线程id  参数2 回调函数返回值 tfun4
    ret = pthread_join(tid,(void**)&retval);
    if(ret!=0){
      perror("pthread_create erro");
        exit(-1);   // 等价于 return -1 
    }
    // 返回值类型 -- return (void*)1100;
     printf("ch=%c, var=%d, str=%s\n", retval->ch,retval->var,retval->str);

     free(retval);


    pthread_exit((void*)1);
//   return 0;
}

方式2:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>


typedef struct 
{
  char ch;
  int var;
  char str[64];
}exit_t;

/**
 * pthread_join 阻塞回收子线程 相当有 wait
 */
void* tfun4(void* args){
  //exit_t* retvar = (exit_t*) malloc(sizeof(exit_t));
  exit_t* retvar= (exit_t*)args;
  retvar->ch='m';
  retvar->var=3000;
  strcpy(retvar->str,"my thread\n");

  pthread_exit((void*)retvar);
}



int main() {
  pthread_t tid;
  exit_t *retval = malloc(sizeof(exit_t));
  //  成功返回0
    int ret= pthread_create(&tid, NULL, tfun4, (void*) retval);  // 返回线程id
    if(ret !=0){
      perror("pthread_create erro");
      exit(-1);
    }


 
    // 参数类型void**
    // 参数1 线程id  参数2 回调函数返回值 tfun4
    ret = pthread_join(tid,(void**)&retval);
    if(ret!=0){
      perror("pthread_create erro");
        exit(-1);
    }
    // 返回值类型 -- return (void*)1100;
     printf("ch=%c, var=%d, str=%s\n", retval->ch,retval->var,retval->str);

     free(retval);


    pthread_exit((void*)1);
//   return 0;
}

2.4.1 回收多个线程

void* tfun5(void* args){
	return args;
}
int main0000019() {
	pthread_t tid[10];
		int ret=0;
		for (int i = 0; i < 10; i++) {
			ret= pthread_create(&(tid[i]), NULL, tfun5, (void*) i);  // 返回线程id
			if (ret == -1) {
				perror("pthread_create fail");
				exit(-1);
			}
		}
		 int *retval;
		sleep(1);
		for(int j=0;j<10;j++){
			pthread_join(tid[j],(void**)&retval);
			printf("-sleep---%d----%d\n",tid[j],retval);
		}
	return 0;
}

2.5.pthread_cancel  

功能: 杀死线程

void* tfun11(void* args){
	while(1){
		int j=(int )args;
		     /**
			 *  调用pthread_cancel 以后如果子线程不进入系统调用比如
			 *  没有调用printf、sleep函数那么无法杀死子线程
			 *  如果没有上面函数,自己添加取消点:
			 *  调用: pthread_testcancel();
			 */
//		printf("son thread...tfun1.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),j);
//	    sleep(1);
		 pthread_testcancel();
	}

	return NULL;
}
/**
 * pthread_cancel 相当于kill
 */
int main0000020() {
	pthread_t tid;
	int ret=0;
	ret= pthread_create(&tid, NULL, tfun11, (void*) 1999);  // 返回线程id
	if (ret !=0 ) {
		perror("pthread_create fail");
		exit(-1);
	}
	int *retval;
	// 杀死线程
	/**
	 *  调用pthread_cancel 以后如果
	 */
	pthread_cancel(tid);
	// 如果线程被杀死了,返回-1
	pthread_join(tid,&retval);
	printf("return value-----  %d",retval);
	//sleep(10);
	return 0;
}

2.5.pthread_detach

功能: 线程主动与主控线程断开关系, 线程结束后,不会产生僵尸线程, 自动释放,网络,多线程常用

void* tfun12(void* args){
		int j=(int )args;
		printf("son thread...tfun1.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),j);
	    sleep(1);
	    return NULL;
}
/***
 * pthread_detach
 */
int main0000021() {
	pthread_t tid;
		 int ret= pthread_create(&tid, NULL, tfun12, (void*) 1999);  // 返回线程id
		if (ret !=0 ) {
		 fprintf(stderr,"pthread_create fail--%s \n",strerror(ret));
			exit(1);
		}
		printf("---%d\n",ret);
		ret= pthread_detach(tid); //设置线pthread_join程分离,0表示成功
		printf("---%d\n",ret);
		// 分离开以后,线程执行完毕会被自动回收tid,没有了,后面pthread_join 来回收会出错
		if(ret!=0){
			 fprintf(stderr,"pthread_detach fail--%s \n",strerror(ret));
						exit(1);
		}

		sleep(3);
		// 功能判断是否分离开,分离,已经回收返回-1
	    int ret3 = pthread_join(tid,NULL);
		printf("---%d\n",ret3);
		//   #define EINVAL      22  /* Invalid argument */
	    if(ret3!=0){
	    	// strerror 无效,没有内容????
	    	 printf("%s\n",strerror(ret3));
	    }

	return 0;
}

2.6.线程属性设置

线程默认属性已经够了,如果要用到 

    1. 线程分离
    2. 线程栈大小, 一个进程默认8M, 2个线程, 每个4M
一般可以创建500 多个线程,一个进程


void* tfun12(void* args){
		int j=(int )args;
		printf("son thread...tfun1.... pid = %d ...  %lu-args--%d \n",getpid(),pthread_self(),j);
	    sleep(1);
	    return NULL;
}

                        

/***
 *  线程属性设置
 *  例子:通过线程属性设置线程自动detach
 *   设置线程 内存大小,必须是 4K 的倍数, 最下是 16K  , ios 下
 */
int main() {
    pthread_t pid;
    pthread_attr_t attr,newattr;
    // 获取线程属性
    int ret = pthread_attr_init(&attr);
    if(ret!=0){
    	printf("pthread_attr_init--%s",strerror(ret));
    	exit(1);
    }
// 宏 PTHREAD_CREATE_DETACHED 分离状态 
    ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); // 设置线程分离属性
    if(ret!=0){
     	printf("pthread_attr_init--%s",strerror(ret));
     	exit(1);
     }
    pthread_t tid;
    ret = pthread_create(&tid,&attr,tfun12,NULL);
    if(ret!=0){
        	printf("pthread_create--%s",strerror(ret));
        	exit(1);
     }
    printf("main ..... pid:%d,tid=%lu\n",getpid(),pthread_self());

    // 获取线程属性
   // pthread_attr_getdetachstate(&newattr,PTHREAD_CREATE_DETACHED);



// 去掉上面修改的,返回默认状态线程属性的 
    ret = pthread_attr_destroy(&attr);
    if(ret!=0){
           	printf("pthread_attr_destroy--%s",strerror(ret));
           	exit(1);
        }
    sleep(5);
    // 验证设置是否有效,出错表示有效
    printf("----pthread_join 1---%d",ret);
// 这里没有返回 -1 , pthread_cancle的时候返回-1 
// 这里放回 22 ,  不合法参数异常   strerror
    ret = pthread_join(tid,NULL);
    printf("----pthread_join 2---%d",ret);
    if(ret!=0){
               	printf("pthread_join--%s",strerror(ret));
               	exit(1);
            }
	return 0;
}

案例2: 修改在堆上申请内存 线程创建

查看项目库版本:getconf GNU_LIBPTHREAD_VERSION, 查看线版本, 有时候版本不一致可能错误

线程使用注意:

1. 主线程退其他线程不退出,主线程应调用pthread_exit
避免僵尸线程方法
1. pthread_join    回收
2. pthread_detach  通过函数分离
3. pthread_creat 设置属性分离
被 join 线程可能在join函数放回前就释放自己的所有内存资源, 所以不应当返回被回收的线程的值
3. malloc 和 mmap 申请的内存肯恩被其他线程释放
4. 避免在多线中fork , 否则 , 子线程中只有调用fork的线程存在,其他线程在子线程中均被
pthread_exit
5. 信号的复杂语义很难和多线程共存,应避免多线程中使用信号

  3. 锁

  线程同步:  多个动作,这个动作完成,才下一个动作 ,有序进行
     多个线程访问共享数据的时候有次序,避免数据混乱
	
  如何避免公共数据、堆数据多线程访问混乱:
     所有线程应该访问公共数据前先拿到锁在访问,锁 

线程1访问资源1 , 线程1拿到锁访问中,线程2过来了没有锁,排队, 
线程1释放锁,线程2持有锁, 才访问
线程3来了,线程3是可以访问资源的,
所以linux的锁不是强制的, 线程3可以访问访问资源 , Linux的叫做建议锁

比如填充下面
111111111   线程1失去cpu   线程3来了获取cpu 
8888888888888888   线程3失去cpu ,线程1获取cpu 
8888888881111111   线程1从原来的位置填充

最终内存数据错乱

2.7.1.  互斥锁

案例1: 子线程打印小写的helloworld  主线程打印大写的HELLOWORLD ,打印错乱

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>

void* tfn(void * arg){
  srand(time(NULL));
  while(1){
    printf("hello ");
    sleep(rand()%3);  //
    printf("world\n");
    sleep(rand()%3);   //
  }
}

int main() {
    pthread_t tid;
    srand(time(NULL));
  int ret=pthread_create(&tid,NULL,tfn,NULL);
  if(ret !=0){
     printf("%s\n",strerror(ret));
     exit(1);
  }
  while(1){
    printf("HELLLO "); 
    sleep(rand()%3);
    printf("WORLD\n");
    sleep(rand()%3);
  }
  pthread_join(tid,NULL); 

  return 0;
}

上面问题解决,加锁:

锁在程序中如何表现出来, mutex叫做 , 初始化互斥锁,可以理解初始值为1
   // 加锁--1     0
   // 解锁 ++1     +1
   如果锁已经被A线程占用了(mutex=0),B线程去拿,那么B线程会阻塞

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>


pthread_mutex_t mutex;  // 1.定义互斥锁 , 使用方式,按照序号

void* tfn(void * arg){
	srand(time(NULL));
	while(1){
		pthread_mutex_lock(&mutex);
		printf("hello ");
		sleep(rand()%3);  //
		printf("world\n");
		pthread_mutex_unlock(&mutex);
		sleep(rand()%3);   //
	}
}



int main(){

	pthread_t tid;
	srand(time(NULL));

    pthread_mutex_init(&mutex,NULL);   //3. 初始化互斥锁,可以理解初始值为1
// init 完了, mutex=1 , mutex是一个结构体,完了方便理解,当做1 

	int ret=pthread_create(&tid,NULL,tfn,NULL);
	if(ret !=0){
		 printf("%s\n",strerror(ret));
		 exit(1);
	}

	while(1){
		pthread_mutex_lock(&mutex);    // 4.加锁--1     0,如果锁被其他线程占用了,那么会阻塞在这里,等到其他线程释放锁
		// try_lock  上面api会阻塞,尝试加锁,加锁失败直接返回错误号,不阻塞
		printf("HELLLO "); 
		sleep(rand()%3);
		printf("WORLD\n");
		pthread_mutex_unlock(&mutex);  //5. 解锁 ++1     +1
		sleep(rand()%3);
	}

	pthread_join(tid,NULL);   // 子线程死循环,杀不死,pthread_cancle(tid) 
	pthread_mutex_destroy(&mutex);     // 6. 释放锁, 用完了,销毁掉

	return 0;
}

死锁:

为什么会产生死锁:

1. 对同一份资源锁2次

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>


pthread_mutex_t mutex;  // 1.定义互斥锁 , 使用方式,按照序号

void* tfn(void * arg){
  srand(time(NULL));
  while(1){
    pthread_mutex_lock(&mutex);
    printf("hello ");
    sleep(rand()%3);  //
    printf("world\n");
    pthread_mutex_lock(&mutex);   // 对同一个互斥量加锁2次 
    sleep(rand()%3);   //
  }
}



int main(){

  pthread_t tid;
  srand(time(NULL));

    pthread_mutex_init(&mutex,NULL);   //3. 初始化互斥锁,可以理解初始值为1
// init 完了, mutex=1 , mutex是一个结构体,完了方便理解,当做1 

  int ret=pthread_create(&tid,NULL,tfn,NULL);
  if(ret !=0){
     printf("%s\n",strerror(ret));
     exit(1);
  }

  while(1){
    pthread_mutex_lock(&mutex);    // 4.加锁--1     0,如果锁被其他线程占用了,那么会阻塞在这里,等到其他线程释放锁
    // try_lock  上面api会阻塞,尝试加锁,加锁失败直接返回错误号,不阻塞
    printf("HELLLO "); 
    sleep(rand()%3);
    printf("WORLD\n");
    pthread_mutex_unlock(&mutex);  //5. 解锁 ++1     +1
    sleep(rand()%3);
  }

  pthread_join(tid,NULL);   // 子线程死循环,杀不死,pthread_cancle(tid) 
  pthread_mutex_destroy(&mutex);     // 6. 释放锁, 用完了,销毁掉

  return 0;
}

    pthread_mutex_lock(&mutex);
    printf("hello ");
    sleep(rand()%3);  //
    printf("world\n");
    pthread_mutex_lock(&mutex);   // 对同一个互斥量加锁2次 , 不释放, 卡死了

如何避免 : 第二次加锁的时候首先解锁,在加锁

 2.  产生条件2,   有2把锁, 有2个共享数据,

线程 t1 持有A 资源,  线程 t2 持有 B 资源,   t2 当 拿着 B锁去请求A ,  t1拿着A去请求 B

就像给给钱给东西, 首先给钱,给东西,  首先给东西,给钱

解决: 第二次访问第二把锁的时候, 不调用lock,调用try-lock ,加锁失败,  等着,放弃已经有的锁,那么别人可以访问你了 (首先都释放,在互相访问)

2.7.2.  读写锁

上面是互斥锁 , 读写锁是一把锁, 既能以读的方式对变量加,又能以为写的方式对变量加,效率ga

原则:    读共享、 写读占、 锁只有一把、写优先级高
                读锁: 读方式加锁
                写锁:写方式加锁

掌握三条原则:

 上图T1 加锁成功, 正在访问, T2  T3  T4 来了, T2 T4 可以加锁,读共享,写独占

 上图 : T1 写独占, t2,t3, t4来了,T1 独占    T1写完了,释放内存,  T3 首先访问, 写优先级高, 访问完才  T2 T4 读访问

 

代码实现读写锁:

pthread_rwlock_rdlock:  读锁

pthread_rwlock_wrlock : 写锁

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
 
int counter;
pthread_rwlock_t rwlock;  // 读写锁
 
 
void* th_write(void* arg){
   int t;
   int i= (int)arg;
   while(1){
     pthread_rwlock_wrlock(&rwlock);  // 写锁
     t= counter;
     sleep(1);
     printf("======write %d: %lu : counter= %d ++counter=%d\n",i,pthread_self(),t,++counter);
     pthread_rwlock_unlock(&rwlock);
     sleep(5);
   }
}
 
void * th_read(void* arg){
     int i= (int)arg;
     while(1){
       pthread_rwlock_rdlock(&rwlock);   // 读锁
       printf("======read %d: %lu : counter= %d \n",i,pthread_self(),counter);
       pthread_rwlock_unlock(&rwlock);
       sleep(2);
     }
 
}
 
int main(){
 
    int i;
    pthread_t tid[8];

    pthread_rwlock_init(&rwlock,NULL);
 

 // 3个写线程
    for(i=0;i<3;i++){
      pthread_create(&tid[i],NULL,th_write,(void*)i);
    }
// 5个读线程 
    for(i=0;i<5;i++){
          pthread_create(&tid[i+3],NULL,th_read,(void*)i);
    }
 
    for(i=0;i<8;i++){
       pthread_join(tid[i],NULL);
    }
 // 只有一把锁,直接释放
    pthread_rwlock_destroy(&rwlock);
  return 0;
}

日志:

 2.8.死锁:

 1. 对一个锁反复lock
   lock mutex=0, 那么 在调用 lock的时候,阻塞,0不能减, 在即没有时间调用解锁了,阻塞在这里

 2. 2个线程,各持有一把锁,去请求另外一把

 

 代码实现????  

4. 解决生产者消费者模式

   4.1 条件变量解决

条件 变量相关函数:
 pthread_cond_init    函数    初始化 pthread_cond_t
 pthread_cond_destory  函数   上面 pthread_cond_t用完了, destory销毁掉
    
        
 pthread_cond_wait   函数
        	pthread_cond_wait(pthread_con_t , 互斥锁 );
 有3个作用:
  1. 等待条件变量满足, 如果条件变量不满足,那么一直在这里等, 
  2. 释放互斥锁  phread_mutex_unlock
  3. 条件变量满足,唤醒, 解除阻塞并重新申请获取互斥锁 
    
  
 pthread_cond_timedwait  函数  
    阻塞5s, 5s没有唤醒,返回了
 pthread_cond_signal 函数
    条件变量满足,唤醒阻塞在条件变量的线程,唤醒一个
 pthread_cond_broadcast函数
    广播,唤醒所有 ,和上面一样
  

上面6个函数 返回值都是 :  成功 0  失败放回错误号 

    4.1.1 一个生产者一个消费者


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>

/* 链表作为共享数据,需被互斥保护 */
struct msg{
	struct msg* next;
	int num;
};
struct msg *head;

int all=0;

/* 静态初始化一个条件变量 */
pthread_cond_t has_product=PTHREAD_COND_INITIALIZER;
/* 一个互斥变量 */
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;   
//静态初始化,等于pthread_cond_init() 这个是动态初始化
//  消费者
void * consumer(void* p){
  struct msg* mp;
  for(;;){
    pthread_mutex_lock(&lock);
//    while(head==NULL){
//    	pthread_cond_wait(&has_product,&lock);
//    }
//    mp = head;
//    head= mp->next;

    if(all==0){
    	// 参数: 条件变量和互斥锁
    	// 1.阻塞等待条件变量,释放互斥锁lock , 让生产者生产
    	pthread_cond_wait(&has_product,&lock);
    	// pthread_cond_timedwait()  设置超是阻塞
    	// 2. 条件满足了->pthread_cond_signal 唤醒的时候,解除阻塞,唤醒互斥锁
    	sleep(3);
    }
    all--;
    pthread_mutex_unlock(&lock);
    printf("-Consume %lu----   %d\n",pthread_self(),all);;
   // free(mp);
    sleep(rand()%5);
  }
}

void * producer(void* p){
  struct msg* mp;
  for(;;){
//	mp= malloc(sizeof(struct msg));
//	mp->num= rand()%100 +1; // 模拟生产一个产品
//	printf("---Product ---------------%d\n",mp->num);
//
//
   pthread_mutex_lock(&lock);
//    mp->next = head;
//    head= mp;
	  all++;
	printf("---Product ---------------%d\n",all);
    pthread_mutex_unlock(&lock);

    pthread_cond_signal(&has_product);
    // 唤醒阻塞在条件变量的至少一个线程
    //pthread_cond_broadcast();   唤醒阻塞在条件变量的所有线程
    sleep(rand()%4);
  }
}
int main(){
    pthread_t pid,cid,cid1;
    srand(time(NULL));

    pthread_create(&pid,NULL,producer,NULL);

    // 多个消费者
    pthread_create(&cid,NULL,consumer,NULL);
  //  pthread_create(&cid1,NULL,consumer,NULL);

    pthread_join(pid,NULL);
    pthread_join(cid,NULL);
  //  pthread_join(cid1,NULL);

	return 0;
}

     4.1.2 一个生产者多个消费者


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>

/* 链表作为共享数据,需被互斥保护 */
struct msg{
	struct msg* next;
	int num;
};
struct msg *head;

int all=0;

/* 静态初始化一个条件变量 */
pthread_cond_t has_product=PTHREAD_COND_INITIALIZER;
/* 一个互斥变量 */
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;   //静态初始化,等于pthread_mutex_t

void * consumer(void* p){
  struct msg* mp;
  for(;;){
    pthread_mutex_lock(&lock);
//    while(head==NULL){
//    	pthread_cond_wait(&has_product,&lock);
//    }
//    mp = head;
//    head= mp->next;

    if(all==0){
    	// 参数: 条件变量和互斥锁
    	// 1.阻塞等待条件变量,释放互斥锁lock
    	pthread_cond_wait(&has_product,&lock);
    	// pthread_cond_timedwait()  设置超是阻塞
    	// 2. 条件满足了->pthread_cond_signal 唤醒的时候,解除阻塞,唤醒互斥锁
    	sleep(3);
    }
    all--;
    pthread_mutex_unlock(&lock);
    printf("-Consume %lu----   %d\n",pthread_self(),all);;
   // free(mp);
    sleep(rand()%5);
  }
}

void * producer(void* p){
  struct msg* mp;
  for(;;){
//	mp= malloc(sizeof(struct msg));
//	mp->num= rand()%100 +1; // 模拟生产一个产品
//	printf("---Product ---------------%d\n",mp->num);
//
//
   pthread_mutex_lock(&lock);
//    mp->next = head;
//    head= mp;
	  all++;
	printf("---Product ---------------%d\n",all);
    pthread_mutex_unlock(&lock);

    pthread_cond_signal(&has_product);
    // 唤醒阻塞在条件变量的至少一个线程
    //pthread_cond_broadcast();   唤醒阻塞在条件变量的所有线程
    sleep(rand()%4);
  }
}
int main(){
    pthread_t pid,cid,cid1;
    srand(time(NULL));

    pthread_create(&pid,NULL,producer,NULL);

    // 多个消费者
    pthread_create(&cid,NULL,consumer,NULL);
    pthread_create(&cid1,NULL,consumer,NULL);
    pthread_create(&cid1,NULL,consumer,NULL);
    pthread_create(&cid1,NULL,consumer,NULL);
    pthread_join(pid,NULL);
    pthread_join(cid,NULL);
    pthread_join(cid1,NULL);

	return 0;
}

   出现错误数据:


 

 why 为什么会出现错误数据?  分析

 改成If问题: 条件 (  if(all==0) )
     如果有多个消费者,那么多个消费值同时执行这段代码,
     消费者A,pthread_mutex_lock(&lock); 获取锁, 消费者B阻塞在这把锁上,
     wait 释放锁,生产者获取锁生产 all=1, 释放锁
     消费A型号获取锁,B阻塞,  all=0 , 释放锁,B获取锁, all=0 , pthread_cond_wait 逻辑正确
     错误逻辑:
     此时消费者B获取锁,那么all=1,   all--; pthread_mutex_unlock(&lock); 执行以后,all=0,
     在if中的消费者A, 正确的,while(all=0) 继续阻塞等待
     那么如果if=0, 往下走 all--, 出现错误数据

4.1.3.正确代码:


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>

/* 链表作为共享数据,需被互斥保护 */
struct msg{
	struct msg* next;
	int num;
};
struct msg *head;

int all=0;

/* 静态初始化一个条件变量 */
pthread_cond_t has_product=PTHREAD_COND_INITIALIZER;
/* 一个互斥变量 */
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;   //静态初始化,等于pthread_mutex_t

void * consumer(void* p){
  struct msg* mp;
  for(;;){
    pthread_mutex_lock(&lock);
//    while(head==NULL){
//    	pthread_cond_wait(&has_product,&lock);
//    }
//    mp = head;
//    head= mp->next;

    while(all==0){    // 把这里的if改成while,重复检测
    	// 参数: 条件变量和互斥锁
    	// 1.阻塞等待条件变量,释放互斥锁lock
    	pthread_cond_wait(&has_product,&lock);
    	// pthread_cond_timedwait()  设置超是阻塞
    	// 2. 条件满足了->pthread_cond_signal 唤醒的时候,解除阻塞,唤醒互斥锁
    	sleep(3);
    }
    all--;
    pthread_mutex_unlock(&lock);
    printf("-Consume %lu----   %d\n",pthread_self(),all);;
   // free(mp);
    sleep(rand()%5);
  }
}

void * producer(void* p){
  struct msg* mp;
  for(;;){
//	mp= malloc(sizeof(struct msg));
//	mp->num= rand()%100 +1; // 模拟生产一个产品
//	printf("---Product ---------------%d\n",mp->num);
//
//
   pthread_mutex_lock(&lock);
//    mp->next = head;
//    head= mp;
	  all++;
	printf("---Product ---------------%d\n",all);
    pthread_mutex_unlock(&lock);

    pthread_cond_signal(&has_product);
    // 唤醒阻塞在条件变量的至少一个线程
    //pthread_cond_broadcast();   唤醒阻塞在条件变量的所有线程
    sleep(rand()%4);
  }
}
int main(){
    pthread_t pid,cid,cid1;
    srand(time(NULL));

    pthread_create(&pid,NULL,producer,NULL);

    // 多个消费者
    pthread_create(&cid,NULL,consumer,NULL);
    pthread_create(&cid1,NULL,consumer,NULL);
    pthread_create(&cid1,NULL,consumer,NULL);
    pthread_create(&cid1,NULL,consumer,NULL);
    pthread_join(pid,NULL);
    pthread_join(cid,NULL);
    pthread_join(cid1,NULL);

	return 0;
}


   
4.2  信号量解决生产者消费模式??

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值