【浅析linux应用编程之线程基础使用】

linux应用编程之线程基础使用


前言

本次分享的是linux应用编程中线程的基础使用,会将常用的一些API进行demo演示。

在前面一篇博文中linux应用编程之高级IO中使用到了阻塞和非阻塞的方式去读取键盘的输入和鼠标的输入,并发效果并不是很好,本篇会先从进程和线程进行两中并发的方式进行键盘和鼠标同时读取demo演示,体会进程和线程的不同之处,在进行线程基础使用的分享。


提示:以下是本篇文章正文内容,下面案例可供参考

一、多进程方式进行鼠标和键盘数据读取

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

//阻塞式IO
int main(void)
{
	//创建子进程,然后在父子进程分别就行键盘的读和鼠标
	int ret = -1;
	int fd = -1;
	char buf[128];
	
	ret = fork();
	if(ret == 0) //子进程
	{
		fd = open("/dev/input/mouse0",O_RDONLY);
		if(fd < 0)
		{
			printf("perror:\n");
			return -1;
		}
		while(1)
		{
			memset(buf,0,sizeof(buf));
			printf("read mouse before\n");
			read(fd,buf,sizeof(buf));
			printf("read text:[%s] \n",buf);
			
		}
	}
	else if(ret > 0) //父进程
	{
		while(1)
		{
			memset(buf,0,sizeof(buf));
			printf("read keyboard before\n");
			read(0,buf,sizeof(buf));
			printf("read text:[%s] \n",buf);
		}
		
	}
	else{
		
	}
	return 0;
}

二、多线程方式进行鼠标和键盘数据读取

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

char buf[128];

void *pthread_func(void * arg)
{
		while(1)
		{
			memset(buf,0,sizeof(buf));
			printf("read keyboard before\n");
			read(0,buf,sizeof(buf));
			printf("read text:[%s] \n",buf);
		}
}
//阻塞式IO
int main(void)
{
	//创建子进程,然后在父子进程分别就行键盘的读和鼠标
	int ret = -1;
	int fd ;
	
	pthread_t th = -1;
	
	
	ret = pthread_create(&th,NULL,pthread_func,NULL);
	if(ret != 0)
	{
		printf("pthread_create error\n");
		return -1;
		
	}
	//因为主线程是死循环 所以可以在这里使用pthread_detach分离子进程
	//子进程分离后就不必要再去回收子线程了
	//主线程
	fd = open("/dev/input/mouse0",O_RDONLY);
	if(fd < 0)
	{
		printf("perror:\n");
		return -1;
	}
		while(1)
		{
			memset(buf,0,sizeof(buf));
			printf("read mouse before\n");
			read(fd,buf,sizeof(buf));
			printf("read text:[%s] \n",buf);
			
		}
	return 0;
}

运行结果

两种方式运行的结果是一样的,不过在实现处理上有很大差别

在这里插入图片描述

三、二者功能实现简单差别

进程

使用进程技术的优势
(1)CPU时分复用,单核心CPU可以实现宏观上的并行
(2)实现多任务系统需求(多任务的需求是客观的)
3.7.1.3、进程技术的劣势
(1)进程间切换开销大
(2)进程间通信麻烦而且效率低

线程

(1)线程技术保留了进程技术实现多任务的特性。
(2)线程的改进就是在线程间切换和线程间通信上提升了效率。
(3)多线程在多核心CPU上面更有优势。
线程的引入
(1)一种轻量级进程
(2)线程是参与内核调度的最小单元
(3)一个进程中可以有多个线程
线程技术的优势
(1)像进程一样可被OS调度
(2)同一进程的多个线程之间很容易高效率通信
(3)在多核心CPU(对称多处理器架构SMP)架构下效率最大化

四、线程的基础使用

4.1 线程常见的函数

线程创建与回收
(1)pthread_create 主线程用来创造子线程的
(2)pthread_join 主线程用来等待(阻塞)回收子线程
(3)pthread_detach 主线程用来分离子线程,分离后主线程不必再去回收子线程
线程取消
(1)pthread_cancel 一般都是主线程调用该函数去取消(让它赶紧死)子线程
(2)pthread_setcancelstate 子线程设置自己是否允许被取消
(3)pthread_setcanceltype
线程函数退出相关
(1)pthread_exit与return退出
(2)pthread_cleanup_push
(3)pthread_cleanup_pop
获取线程id
(1)pthread_self

通过man手册进行相关API使用的查看
 1int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
 2int pthread_join(pthread_t thread, void **retval);

 3int pthread_detach(pthread_t thread);
 4int pthread_cancel(pthread_t thread);
 5int pthread_setcancelstate(int state, int *oldstate);
 6int pthread_setcanceltype(int type, int *oldtype);
 7void pthread_exit(void *retval);
 8void pthread_cleanup_push(void (*routine)(void *),
                                 void *arg);
 9void pthread_cleanup_pop(int execute);
 10pthread_t pthread_self(void);

4.2 线程同步之信号量

任务:用户从终端输入任意字符然后统计个数显示,输入end则结束
使用多线程实现:主线程获取用户输入并判断是否退出,子线程计数

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


char buf[128] = {0};
unsigned char flag = 0;	
sem_t sem;


//子线程程序 用于统计字符个数
void *pthread_func(void *arg)
{
	//子进程首先应该有个循环
	//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取BUf长度
	//打印后再次被阻塞
	sem_wait(&sem) ;
	//while(strncmp(buf,"end",3))
	while(flag == 0)
	{	
		printf("本次输入了%d个字符\n",(unsigned int)strlen(buf));
		memset(buf,0,sizeof(buf));
		sem_wait(&sem) ;
	}
	pthread_exit(NULL);
}

int main(void)
{
	
	int ret = -1;

	pthread_t th = -1;

	
	sem_init(&sem,0,0);
	
	ret = pthread_create(&th,NULL,pthread_func,NULL);
	
	printf("please input str:");
	while(scanf("%s",buf))
	{
		//去比较输入的是不是end 如果是则退出
		if(!strncmp(buf,"end",3))
		{
			printf("program end\n");
			flag = 1;
			sem_post(&sem); //跳出循环后再次激活子线程
			//exit(0);
			break;
		}
		
		//主线程在收到用户输入字符串并确实不是end后 就去发信号激活子线程来计数
		//子线程被阻塞就是同步问题 
		//信号量就可以来实现这个同步
		sem_post(&sem);
	}
		
	//回收子进程
	printf("等待子进程回收\n");
	ret = pthread_join(th,NULL);
	if(ret != 0 )
	{
		printf("子进程回收失败\n");
	}
	printf("子进程回收成功\n");
	sem_destroy(&sem); //销毁
	return 0;
}

注意:在进行编译时要加链接库 -lpthread

在这里插入图片描述

4.3 线程同步之互斥锁

(1)互斥锁又叫互斥量(mutex)
(2)互斥锁和信号量的关系:可以认为互斥锁是一种特殊的信号量
(3)互斥锁主要用来实现关键段保护

//相关函数
pthread_mutex_init      //初始化
pthread_mutex_destroy   //销毁
pthread_mutex_lock      //上锁
pthread_mutex_unlock    //解锁

//条件变量函数
pthread_cond_init		//初始化
pthread_cond_destroy    //销毁
pthread_cond_wait	   //等待
pthread_cond_signal/pthread_cond_broadcast  //信号唤醒
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
#include <pthread.h>
#include <unistd.h>


char buf[128] = {0};
unsigned char flag = 0;	
pthread_mutex_t mutex;


//子线程程序 用于统计字符个数
void *pthread_func(void *arg)
{
	//子进程首先应该有个循环
	//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取BUf长度
	//打印后再次被阻塞
	
	//while(strncmp(buf,"end",3))
	sleep(1); 
	while(flag == 0)
	{	
		pthread_mutex_lock(&mutex);
		printf("本次输入了%d个字符\n",(unsigned int)strlen(buf));
		memset(buf,0,sizeof(buf)); 
		pthread_mutex_unlock(&mutex);
		sleep(1);  //睡眠将锁的资源拿给主线程
	}
	pthread_exit(NULL);
}

int main(void)
{
	
	int ret = -1;

	pthread_t th = -1;

	
	pthread_mutex_init(&mutex,NULL);
	
	ret = pthread_create(&th,NULL,pthread_func,NULL);
	
	printf("please input str:");
	while(1)
	{
		pthread_mutex_lock(&mutex);
		scanf("%s",buf);
		pthread_mutex_unlock(&mutex);
		//去比较输入的是不是end 如果是则退出
		if(!strncmp(buf,"end",3))
		{
			printf("program end\n");
			flag = 1;
			//exit(0);
			break;
		}
		sleep(1);  //睡眠将锁资源拿给子线程
	}
		
	//回收子进程
	printf("等待子进程回收\n");
	ret = pthread_join(th,NULL);
	if(ret != 0 )
	{
		printf("子进程回收失败\n");
	}
	printf("子进程回收成功\n");
	pthread_mutex_destroy(&mutex); //销毁
	return 0;
}

实验结果

在这里插入图片描述

加入条件变量

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


char buf[128] = {0};
unsigned char flag = 0;	
pthread_mutex_t mutex;
pthread_cond_t cond;


//子线程程序 用于统计字符个数
void *pthread_func(void *arg)
{
	//子进程首先应该有个循环
	//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取BUf长度
	//打印后再次被阻塞
	
	//while(strncmp(buf,"end",3))
	//sleep(1); 
	while(flag == 0)
	{	
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex); //等待添加成立后再执行
		printf("本次输入了%d个字符\n",(unsigned int)strlen(buf));
		memset(buf,0,sizeof(buf)); 
		pthread_mutex_unlock(&mutex);
		//sleep(1);  //睡眠将锁的资源拿给主线程
	}
	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_create(&th,NULL,pthread_func,NULL);
	
	printf("please input str:");
	while(1)
	{
		//pthread_mutex_lock(&mutex);
		scanf("%s",buf);
		pthread_cond_signal(&cond);
		//pthread_mutex_unlock(&mutex);
		//去比较输入的是不是end 如果是则退出
		if(!strncmp(buf,"end",3))
		{
			printf("program end\n");
			flag = 1;
			//exit(0);
			break;
		}
		//sleep(1);  //睡眠将锁资源拿给子线程
	}
		
	//回收子进程
	printf("等待子进程回收\n");
	ret = pthread_join(th,NULL);
	if(ret != 0 )
	{
		printf("子进程回收失败\n");
	}
	printf("子进程回收成功\n");
	pthread_mutex_destroy(&mutex); //销毁
	pthread_cond_destroy(&cond); //销毁
	return 0;
}

实验结果

在这里插入图片描述

总结

以上就是linux应用编程之线程基础使用的简单分享,线程的使用在处理多并发的问题上十分重要,当然这里我们只是学会如何去简单的使用多线程,实际应用中肯定会比这个复杂的多,具体问题还得具体来分析。
总之一步一个脚印,先将基础的学会,再去深入学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小殷学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值