嵌入式总结9(2)

多线程编程

线程是任务调度和执行的基本单位。

线程的特点:

线程切换的开销很低——实质是函数的切换
线程通信机制简答——全局变量

线程操作:
1.线程函数是由谁提供的?
非OS,而是线程库libpthread.a/.so
线程控制函数有:pthread_create、pthread_join、pthread_detach、pthread_cancel、pthread_exit等
2.线程库和函数手册的安装
sudo adt-get install glibc-doc:安装线程库
sudo apt-get install manpages-posix-dev:安装线程库的函数手册
3.线程创建
4.线程退出
5.线程等待
6.线程状态

线程同步:
1.线程vs进程
进程:进程空间天然是独立的,因此进程间资源的保护是天然的(现成的),需要重点关心的是进程间通信
线程:多线程天然的共享进程空间,因此线程数据共享是天然的(现成的),需要重点关心的是资源的保护
2.线程的资源保护机制
1)互斥锁(只能在线程里面用)
互斥锁使用的步骤:
定义一个互斥锁(变量)
初始化互斥锁:预设互斥锁的初始值
加锁解锁
进程退出时销毁互斥锁
2)线程信号量
线程信号量使用步骤:
定义信号量集合
初始化集合中的每个信号量
p、v操作
进程结束时,删除线程信号量集合

p操作:

` #include <semaphore.h> 
   int sem_wait(sem_t *sem);//阻塞p操作`

功能:
阻塞p操作集合中某个信号量,值-1
如果能够p操作成功最好,否则就阻塞直到p操作操作成功为止。
返回值:
成功返回0,失败返回-1,errno被设置。
参数:
p操作的某个信号量。
比如:sem_wait(&sem[0]);
sem_wait的兄弟函数
int sem_trywait(sem_t *sem):不阻塞
如果能够p操作就p操作,如果不能p操作就出错返回,不会阻塞。
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
可以设置阻塞时间,如果能够p操作就p操作,不能就阻塞,如果在设置的时间内好没有
p操作成功就是出错返回,不再阻塞。

v操作

#include <semaphore.h>
int sem_post(sem_t *sem);

功能:
对某个信号量进行v操作,v操作不存在阻塞问题。
v操作成功后,信号量的值会+1
返回值:成功返回0,失败返回-1,errno被设置。
代码演示
sem_post(&sem[0]);

3)条件变量(在互斥锁的配合下才能工作)
条件变量的作用(线程协同)
条件变量使用步骤:
· 定义一个条件变量(全局变量)由于条件变量需要互斥锁的配合,所以还需要定义一个线程互斥锁。
· 初始化条件变量

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

功能:
初始化条件变量,与互斥锁的初始化类似。
pthread_cond_t cond; //定义条件变量
pthread_cond_init(&cond, NULL); //第二个参数为NULL,表示不设置条件变量的属性。
也可以直接初始化:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//与互斥锁的初始化的原理是一样的
返回值:成功返回0,失败返回非零错误号
参数:

  • cond:条件变量
  • attr:用于设置条件变量的属性,设置为NULL,表示使用默认属性

· 使用条件变量

等待条件的函数
函数原型
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
功能:
检测条件变量cond,如果cond没有被设置,表示条件还不满足,别人还没有对cond进行设置,此时
pthread_cond_wait会休眠(阻塞),直到别的线程设置cond表示条件准备好后,才会被唤醒。
返回值:成功返回0,失败返回非零错误号
参数
- cond:条件变量
- mutex:和条件变量配合使用的互斥锁

pthread_cond_wait的兄弟函数
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
多了第三个参数,用于设置阻塞时间,如果条件不满足时休眠(阻塞),但是不会一直休眠, 当时间超时后,如果cond还没有被设置,函数不再休眠。

设置条件变量的函数
函数原型
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
功能:
当线程将某个数据准备好时,就可以调用该函数去设置cond,表示条件准备好了, pthread_cond_wait检测到cond被设置后就不再休眠(被唤醒),线程继续运行,使用别的线程准备好的数据来做事。
当调用pthread_cond_wait函数等待条件满足的线程只有一个时,就是用pthread_cond_signal来唤醒,如果说有好多线程都调用pthread_cond_wait在等待时,使用int pthread_cond_broadcast(pthread_cond_t *cond);
它可以将所有调用pthread_cond_wait而休眠的线程都唤醒。

· 删除条件变量,也需要把互斥锁删除。

进程vs线程
1.多线程比多进程成本低,但性能更低
多进程是立体交通系统,虽然造价高,上坡下坡多耗电油,但是不堵车。
多线程是平面交通系统,造价低,但红绿灯太多,老堵车。
2.区别
进程是资源分配的最小单位,线程是任务调度的最小单位;
每个进程拥有独立的地址空间,多个线程共享进程地址空间:
线程之间切换比进程之间切换开销少;
线程的调度必须通过频繁加锁来保持同步,影响了线程并发性能;
进程比线程更健壮,多进程之间相互独立,进程的异常对其他进程无影响,一个线程的崩溃可能影响其他线程或者整个程序;
线程之间的通信更方便(小数据量),同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
多线程的代码结构比多进程代码结构易读;
3.如何选择?
需要频繁创建销毁的优先用线程
高性能交易服务器中间件,如TUXEDO,都是主张多进程的
需要进行大量计算的优先使用线程
强相关的处理用线程,弱相关的处理用进程
多机分布的y用进程,多核分布的用线程

 1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <pthread.h>
  5 #include <string.h>
  6 #include <errno.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <signal.h>
 11 
 12 
 13 #define SECON_PTH_NUMS  2  //次线程数量
 14 #define PTHEXIT         -1      
 15 
 16 
 17 /* 传递给线程的参数 */
 18 typedef struct pthread_arg
 19 {
 20         pthread_t tid;//存放线程tid
 21         int pthno;//我自己定义的编号
 22         int fd;//文件描述符
 23 }ptharg;
 24 
 25 
 26 
 27 struct gloable_va
 28 {
 29         ptharg pth_arg[SECON_PTH_NUMS];//结构体数组,每个元素会被当做参数传递给对应的次线程
 30         int pth_exit_flg[SECON_PTH_NUMS];//每个元素存放对应编号线程的退出状态
 31         pthread_attr_t attr;//存放线程新属性
 32 }glbva;
 33 
 34 void print_err(char *str, int line, int err_no)
 35 {
 36         printf("%d, %s:%s", line, str, strerror(err_no));
 37         exit(-1);
 38 }
 39 
 40 /* 线程退出处理函数 */
 41 void pth_exit_deal(void *arg)
 42 {
 43         pthread_t tid = ((ptharg *)arg)->tid;
 44 
 45         printf("!!! pthread %lu exit\n", tid);
 46 }
 47 
 48 void *pth_fun(void *pth_arg)
 49 {
 50         int fd = ((ptharg *)pth_arg)->fd;
 51         int pthno = ((ptharg *)pth_arg)->pthno;
 52         pthread_t tid = ((ptharg *)pth_arg)->tid;
 53 
 54         //pthread_detach(pthread_self());//线程把自己分离出去
 55 
 56         //注册线程退出处理函数
 57         pthread_cleanup_push(pth_exit_deal, pth_arg);
 58 
 59         printf("pthno=%d, pthread_id=%lu\n", pthno, tid);
 60 
 61         while(1)
 62         {
 63                 write(fd, "hello ", 6);
 64                 write(fd, "world\n", 6);
 65                 //检测退出状态
 66                 if(glbva.pth_exit_flg[pthno] == PTHEXIT) break;
 67         }
 68 
 69 
 70         pthread_cleanup_pop(!0);
 71         return NULL;
 72         pthread_exit((void *)10);
 73 }
 74 
75 void signal_fun(int signo)
 76 {
 77         if(SIGALRM == signo)
 78         {
 79                 int i = 0;
 80                 for(i=0; i<SECON_PTH_NUMS; i++)
 81                 {
 82                         //pthread_cancel(glbva.pth_arg[i].tid);//取消次线程
 83                         glbva.pth_exit_flg[i] = PTHEXIT;//设置为退出状态
 84                 }
 85         }
 86         else if(SIGINT == signo)
 87         {
 88                 exit(0);
 89         }
 90 }
 91 
 92 void process_exit_deal(void)
 93 {
 94         /* 销毁线程的属性设置 */
 95         int ret = 0;
 96         ret = pthread_attr_destroy(&glbva.attr);
 97         if(ret != 0) print_err("pthread_attr_destroy fail", __LINE__, ret);
 98 
 99         printf("\nprocess exit\n");
100 }
101 
102 int main(void)
103 {
104         int fd = 0;
105         int i = 0;
106         int ret = 0;
107 
108         //注册进程退出处理函数,exit正常终止进程时弹栈调用
109         atexit(process_exit_deal);
110 
111         //打开文件,供线程操作,所有的线程(函数)可以共享打开的文件描述符
112         fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);
113         if(fd == -1) print_err("open ./file fail", __LINE__, errno);
114 
115         /* 初始化护attr, 设置一些基本的初始值 */
116         ret = pthread_attr_init(&glbva.attr);
117         if(ret != 0) print_err("pthread_attr_init fail", __LINE__, ret);
118 
119         /* 设置分离属性 */
120         ret = pthread_attr_setdetachstate(&glbva.attr, PTHREAD_CREATE_DETACHED);
121         if(ret != 0) print_err("pthread_attr_setdetachstate fail", __LINE__, ret);
122 
123         /* 通过循环创建两个次线程 */
124         for(i=0; i<SECON_PTH_NUMS; i++)
125         {
126                 glbva.pth_arg[i].fd = fd;//保存文件描述符
127                 glbva.pth_arg[i].pthno = i; //我自己给的线程编号
128                                                            //创建好次线程后,讲次线程分离
129                 ret = pthread_create(&glbva.pth_arg[i].tid, &glbva.attr, pth_fun, (void *)&glbva.pth_arg[i]);
130                 if(ret != 0) print_err("pthread_create fail", __LINE__, ret);
131         }
132 
133         printf("main tid = %lu\n", pthread_self());
134 
135         signal(SIGINT, signal_fun);
136 
137         /* 定时5秒,时间到后取消次线程 */
138         signal(SIGALRM, signal_fun);
139         alarm(3);
140 
141         #if 0
142         void *retval = NULL;
143         for(i=0; i<SECON_PTH_NUMS; i++)
144         {
145                 //阻塞等待此线程结束,回收次线程资源,并通过第二个参数接受返回值
146                 pthread_join(glbva.pth_arg[i].tid, &retval);
147                 printf("@@ %ld\n", (long)retval);
148         }
149         #endif
150 
151 
152         while(1)
153         {
154                 write(fd, "hello ", 6);
155                 write(fd, "world\n", 6);
156         }
157 
158         return 0;
159 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值