一、再论进程
1、多进程实现同时读取键盘和鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
// 思路就是创建子进程,然后父子进程中分别进行读键盘和鼠标的工作
int ret = -1;
int fd = -1;
char buf[200];
ret = fork();
if (ret == 0)
{
// 子进程
fd = open("/dev/input/mouse1", O_RDONLY);
if (fd < 0)
{
perror("open:");
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)
{
// 父进程
while (1)
{
memset(buf, 0, sizeof(buf));
printf("before read.\n");
read(0, buf, 5);
printf("读出键盘的内容是:[%s].\n", buf);
}
}
else
{
perror("fork:");
}
return 0;
}
2、分析优劣
使用进程技术的优势
:
(1)CPU时分复用,单核心CPU可以实现宏观上的并行
(2)实现多任务系统需求(多任务的需求是客观的)
进程技术的劣势
:
(1)进程间切换开销大 (每一个进程都有自己独立的地址空间)
(2)进程间通信麻烦而且效率低
二、解决方案就是线程技术
线程的优势
(1)线程技术保留了进程技术实现多任务的特性。
(2)线程的改进就是在线程间切换和线程间通信上提升了效率。
(3)多线程在多核心CPU上面更有优势。
(1)像进程一样可被OS调度
(2)同一进程的多个线程之间很容易高效率通信
(3)在多核心CPU(对称多处理器架构SMP)架构下效率最大化
1、线程的引入
简介
(1)一种轻量级进程
(2)线程是参与内核调度的最小单元
(3)一个进程中可以有多个线程
2、线程常见函数
线程创建与回收
(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
3、使用线程技术同时读取键盘和鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
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;
ret = pthread_create(&th, NULL, func, NULL);
if (ret != 0)
{
printf("pthread_create error.\n");
return -1;
}
// 因为主线程是while(1)死循环,所以可以在这里pthread_detach分离子线程
pthread_detach(&th);
// 主任务
fd = open("/dev/input/mouse1", O_RDONLY);
if (fd < 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;
}
4、pthread_cleanup_push 和 pthread_cleanup_pop
作用一:防止线程拿到锁后被其他线程给取消或杀死掉导致程序死锁
线程可以安排他退出时需要调用的函数,这与进程可以用
atexit函数
安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说他们的执行顺序与他们注册的顺序相反
。
void *func(void *arg)
{
pthread_mutex_lock(&mutex);//拿锁
//将清理函数压入栈中,如果线程退出后将被唤醒
pthread_cleanup_push(clean_func,arg);
//子线程操作
//子线程在这里可能被主线程cancle
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);// 0 移除清理程序不执行 非0 移除清理程序但不执行
}
void clean_func()
{
pthread_mutex_unlock(&mutex);
}