• linux线程大杂烩==Linux应用编程6


    一、再论进程–资源分配的最小单位

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define NAME "/dev/input/mouse1"
    int main()
    {
    	// 思路为,创建子进程,然后分别进行读键盘(父亲)和鼠标(儿子)工作
    	int ret=-1;int fd=-1;char buf[200];
    	ret=fork();
    	if(ret==0){//child
    		fd=open(NAME,O_RDONLY);
    		if(fd<0)
    			return -1;
    		while(1){
    			memset(buf,0,sizeof(buf));//口袋清空做备放数据
    			printf("before read.\n");
    			read(fd,buf,50);
    			printf("读出鼠标的内容是:[%s].\n",buf);
    		}	
    	}
    	else if(ret>0){//parent
    		while(1){
    			memset(buf,0,sizeof(buf));
    			printf("before read.\n");
    			read(0,buf,5);
    			printf("读出键盘的内容是:[%s].\n",buf);
    		}
    	}
    	else
    		perror("fork");return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2、使用进程技术的优势

    • (1)CPU 分时复用,单核心 CPU 可以实现宏观上的多任务并行。

    3、使用进程技术的劣势

    • (1)进程间切换开销大。
    • (2)进程间通信(IPC)麻烦且效率低下。

    4、解决方案——线程技术

    • (1)线程技术保留来进程实现多任务的特性。
    • (2)线程的改进就是中线程间切换和通信上提升来效率,继承来进程的优点,克服来进程 的缺点。
    • (3)多线性中多 CPU 核心上更具优势,因为每个核心可以处理一个线程(同一时刻)

    二、线程的引入

    1、使用线程技术同时读取键盘和鼠标

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    char buf[200];
    void *func(void* arg)
    {
    	while(1){
    		memset(buf,0,sizeof(buf));
    		printf("before read.\n");
    		read(0,buf,5);
    		printf("读出键盘的内容是:[%s].\n", buf);
    	}
    }
    int main(void)
    {
    	int ret=-1;int fd=-1;
    	pthread_t th=-1;//线程id
    	ret=pthread_create(&th,NULL,func,NULL);//创建线程-子线程键盘
    	if(ret<0){
    		perror("open");return -1;
    	}
    	while(1){//主线程鼠标
    		memset(buf,0,sizeof(buf));
    		printf("before read.\n");
    		read(fd,buf,50);
    		printf("读出鼠标的内容是:[%s].\n",buf);
    	}	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2、Linux 中的线程简介

    • (1)线程是一种轻量级进程。
    • (2)线程是参与内核调度的最小单元。
    • (3)一个进程中可以有多个线程。

    3、线程技术的优势

    • (1)像进程一样可被 OS 调度。
    • (2)同一进程的多个线程之间很容易进行高效率通信。
    • (3)在多核心 CPU 架构(对称多处理器架构 SMP)下效率最大化。

    4、线程间通信方式

    • (1)信号:用 pthread_kill 对线程发信号,目标线程需要用 sigaction 来处理。
    • (2)信号量(计数器):用于同步,一个线程阻塞等待另一个线程给它发信号量才会继续执行
    • (3)锁机制
      • ①互斥锁:用于保护共享数据或保持操作互斥。
      • ②条件变量:与信号量类似,与互斥锁一起使用,对条件的测试是在互斥锁的保护下进行的。条件变量与互斥锁搭配使用
      • ③自旋锁:当锁被另一个线程锁住时,则当前线程则在锁周围“旋转”,反复执行循环指令, 占用 CPU 时间。
      • 读写锁:一种特殊的自旋锁,写独占、读共享,适合于对数据结构读的次数远大于写的情况。

    三、线程常见函数

    1.线程创建与回收

    • pthread_creat:主线程用来创建子线程
    • pthread_join:主线程用来(阻塞)等待回收子线程
    • pthread_detach:主线程用来分离子线程,分离后主线程不必再去回收子线程

    2.线程取消

    • pthread_cancel:其他线程调用该函数去取消(结束)子线程
    • pthread_setcancelstate:子线程用来设置自己是否允许被取消
    • pthread_setcanceltype:子线程用来设置自己取消类型(立即取消or等执行到属于cancellation point 的函数的时候才会取消)

    3.线程函数退出

    • pthread_exit与return:都可用于线程函数退出,注意exit是用于进程退出
    • pthread_cleanup_push与pthread_cleanup_pop:根线程退出时需要调用的清理函数相关

    4.获取线程 ID

    • pthread_self

    5.发送信号

    • pthread_kill

    四、线程同步之信号量

    task1
    用户从终端输入任意字符以回车键结束,程序统计个数并显示,输入“end”则结束。

    • (1)使用多线程实现:主线程获取用户输入并判断是否结束程序,然后给子线程发信号量, 子线程(阻塞)等待信号量然后退出子线程或者计数并打印。
    • (2)信号量的引入:
      • sem_init:信号量初始化
      • sem_post:发送信号量,表示当前状态。
      • sem_wait:即是用来判断,能用就用,不能用就等
      • sem_destroy:销毁信号量
    #include 
    #include 
    #include 
    #include 
    #include 
    char buf[200]={0};
    sem_t sem;
    unsigned int flag=0;
    void* func(void* arg)// 子线程程序,作用是统计 buf 中的字符个数并打印
    {
    	//子线程首先应该有个循环,
    	//循环中阻塞在等待主线程激活的时候,子线程激活后就去获取buf中的字符
    	//长度,然后打印;完成后再次被阻塞
    	sem_wait(&sem);
    	while(flag==0){//while(strncmp(buf,"end",3)!=0)
    		printf("本次输入了%d 个字符\n", strlen(buf));
    		memset(buf,0,sizeof(buf));
    		sem_wait(&sem);
    	}
    	pthread_exit(NULL);//return;皆可用于函数线程退出
    }
    int main()
    {
    	int ret=-1;pthread th=-1;
    	
    	sem_init(&sem,0,0);
    	
    	ret=pthread(&th,NULL,func,NULL);
    	if(ret!=0){
    		perror("pthread");exit(-1);//exit为退出进程函数
    	}
    	printf("输入一个字符串,以回车结束.\n");
    	while(scanf("%s",buf)){
    		// 去比较用户输入的是不是 end,如果是则退出,如果不是则继续
    		if(!strcmp(buf,"end",3)){
    			printf("程序结束\n");flag = 1;
    			sem_post(&sem);
    			break;
    		}
    		//主线程去发信号激活子线程来计数。
    		//子线程被阻塞,主线程可以激活,这就是线程的同步问题
    		sem_post(&sem);
    	}
    	
    	//回收子线程
    	printf("等待回收子线程\n");
    	ret=pthread_join(th,NULL);
    	if (ret != 0){
    		printf("pthread_join error.\n");exit(-1);
    	}
    	printf("子线程回收成功\n");
    	
    	sem_destroy(&sem);return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    五、线程同步之互斥锁

    1.什么是互斥锁

    • (1)互斥锁又叫互斥量,一个互斥量仅可被加锁一次,因此当互斥量被锁上之后,其他互 斥量想加锁就必须(阻塞)等待该互斥量被解锁,因此可用于线程同步。
    • ( 2 ) 相 关 函 数 : pthread_mutex_init 、 pthread_mutex_destory 、 pthread_mutex_lock 、 pthread_mutex_unlock
    • (3)互斥锁和信号量的关系:可认为互斥锁是一种特殊的信号量。
    • (4)互斥锁主要用来实现关键段保护。

    2.用互斥锁实现上节功能

    #include  
    #include  
    #include  
    #include  
    char buf[200] = {0}; 
    pthread_mutex_t mutex; 
    unsigned int flag = 0;
    // 子线程程序,作用是统计 buf 中的字符个数并打印 
    void *func(void *arg) 
    { 	
    	//while (strncmp(buf, "end", 3) != 0) 
    	sleep(1); 
    	while (flag == 0) { 
    		pthread_mutex_lock(&mutex); 
    		printf("本次输入了%d 个字符\n", strlen(buf)); 
    		memset(buf, 0, sizeof(buf)); 
    		pthread_mutex_unlock(&mutex); 
    		sleep(1); 
    	}
    	pthread_exit(NULL); 
    }
    int main()
    {
    	int ret=1-;pthread th=-1;
    	pthread_mutex_init(&mutex,NULL);
    	if (ret != 0) exit(-1);
    	printf("输入一个字符串,以回车结束\n");
    	while (1){
    		pthread_mutex_lock(&mutex);
    		scanf("%s", buf);
    		pthread_mutex_unlock(&mutex);
    		// 去比较用户输入的是不是 end
    		if (!strncmp(buf, "end", 3)){
    			printf("程序结束\n");flag = 1;break;	
    		}
    		sleep(1);
    	}
    	printf("等待回收子线程\n");
    	ret = pthread_join(th, NULL);
    	if (ret != 0) exit(-1);
    	printf("子线程回收成功\n");
    	pthread_mutex_destroy(&mutex);return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    六、线程同步之条件变量

    1.什么是条件变量

    • 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程“(阻塞)等待条件成立而挂起”,另一个线程使“条件成立”
    • 为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

    2.相关函数

    • pthread_cond_init
    • pthread_cond_destroy
    • pthread_cond_wait
    • pthread_cond_signal/pthread_cond_broacast

    3、3.使用条件变量来实现上节功能。

    #include  
    #include  
    #include  
    #include  
    char buf[200] = {0}; 
    pthread_mutex_t mutex;pthread_cond_t cond; //条件变量,互斥锁搭配使用
    unsigned int flag = 0;
    // 子线程程序,作用是统计 buf 中的字符个数并打印
    void* func(void* arg)
    {
    	while(flag==0){
    		pthread_mutex_lock(&mutex);
    		pthread_cond_wait(&cond,&mutex);
    		printf("本次输入了%d 个字符\n", strlen(buf));
    		memset(buf, 0, sizeof(buf));
    		pthread_mutex_unlock(&mutex);
    	}
    	pthread_exit(NULL);
    }
    int main(void)
    {
    	int ret=-1;pthread_t th=-1;
    	pthread_mutex_init(&mutex,NULL);pthread_cond_init(&cond,NULL);
    	
    	ret=pthread_crate(&th,NULL,func,NULL);
    	if(ret!=0) exit(-1);
    	printf("输入一个字符串,以回车结束\n");
    	while (1){
    		scanf("%s", buf);
    		pthread_cond_signal(&cond);
    		// 去比较用户输入的是不是 end,如果是则退出,如果不是则继续
    		if (!strncmp(buf, "end", 3)){
    			printf("程序结束\n");flag = 1;break;
    		}
    	}
    	printf("等待回收子线程\n"); 
    	ret = pthread_join(th, NULL);
    	if (ret != 0) exit(-1);
    	printf("子线程回收成功\n");
    
    	pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    详细线程介绍请看本人另一篇博客线程同步

  • 相关阅读:
    JUC——CopyOnWriteArrayList
    自动化运维工具Ansible教程(一)【入门篇】
    flink 写入 starrocks 报错 too many filtered rows attachment
    Python Numpy.einsum
    C++题解(6) 信息学奥赛一本通:2069:【例2.12 】糖果游戏
    详解非负矩阵分解(NMF)及其在脑科学中的应用
    vue 表单重置功能
    SQL零基础入门教程,贼拉详细!贼拉简单! 速通数据库期末考!(十)
    MySQL进阶02_索引概述_结构_分类_语法
    区块链(1):区块链简介
  • 原文地址:https://blog.csdn.net/weixin_47397155/article/details/126164891