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使用的查看
1、int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
2、int pthread_join(pthread_t thread, void **retval);
3、int pthread_detach(pthread_t thread);
4、int pthread_cancel(pthread_t thread);
5、int pthread_setcancelstate(int state, int *oldstate);
6、int pthread_setcanceltype(int type, int *oldtype);
7、void pthread_exit(void *retval);
8、void pthread_cleanup_push(void (*routine)(void *),
void *arg);
9、void pthread_cleanup_pop(int execute);
10、pthread_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应用编程之线程基础使用的简单分享,线程的使用在处理多并发的问题上十分重要,当然这里我们只是学会如何去简单的使用多线程,实际应用中肯定会比这个复杂的多,具体问题还得具体来分析。
总之一步一个脚印,先将基础的学会,再去深入学习。