最近看了一些视频,看了下高手对多线程编程的操作,他们对共享资源保护手段使用的选择非常谨慎,结合以前的编程经历跟他们对比,才发现自己写的代码是多么糟糕。
这里仅做点心得总结,供自己日后回看,可能有不对的地方。
共享资源就是临界区,以前看注释总是看到临界区,就是不知道啥叫临界区~~
首先来段科普的,这是百度copy:
1、临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。
当有线程进入临界区段时,其他线程或是进程必须等待,有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用。只能被单一线程访问的设备,例如:打印机。
2、临界资源:多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。
3、进程进入临界区的调度原则是:
(1)如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
(2)进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
(3)任何时候,处于临界区内的进程不可多于一个。
(4)如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
扩展资料
1、临界区存在的问题
临界区的退出,不会检测是否是已经进入的线程,也就是说,可以在A线程中调用进入临界区函数,在B线程调用退出临界区的函数,同样是成功。
临界区内的数据一次只能同时被一个进程使用,当一个进程使用临界区内的数据时,其他需要使用临界区数据的进程进入等待状态。
2、各进程采取互斥的方式,实现共享的资源称作临界资源。
属于临界资源的硬件有打印机、磁带机等,软件有消息缓冲队列、变量、数组、缓冲区等。 诸进程间应采取互斥方式,实现对这种资源的共享。
———————————————————————————————————————————
对多线程编程中临界区最大的问题是竞争问题,解决办法很简单,就是互斥同步,对此操作系统提供了多种手段,有关中断、调度器上锁、自旋锁、信号量、互斥锁等,操作系统之所以弄出这么多手段,就是因为他们各有用途,是共存的关系,编程者并不能够一个手段打天下。
使用互斥手段一定要站在线程对cpu抢占的角度看问题,从上面的科普可知,某线程抢占了CPU,其他线程就需要等待,有些线程任务十分着急有些线程任务不着急,如果手段使用不恰当,该紧急的使用耗时长的手段,不紧急的使用时间很短的手段,就会有问题
保护手段既要结合需要保护对象的特性,同时要尽快归还cpu的使用权
下面是常用手段的介绍
关中断、开中断
当访问共享资源的速度很快(读取或写入极少量变量时:如喂狗操作、FLASH写数据、需要和中断服务程序共享变量或数据结构等)以至于访问共享资源所花的时间小于中断关闭时间。
比如喂狗操作,如果中途被其他中断打断,导致喂狗失败,系统直接重启,自然希望从头到位都占用CPU
由于使用该方法会影响中断延迟,所以只有在极其关键的地方才能使用
互斥锁
这是访问共享资源的首选方法。特别是当某些任务对共享资源(常见于硬件的访问,而该硬件的时候也比较耗时间)访问有时间要求时。
其缺点是开销十分大
自旋锁
当访问共享资源的时间比中断关闭时间长,但是却比互斥锁时间短时,不是特别重要代码量也相对比较小,如变量值自加/自减。既能达到互斥功能,又能减少资源开销。
在中断里互斥只能用自旋锁,中断中不能睡眠,而且中断本身就是得快进快出
以下请问使用什么方式保护最为合适。
void T1(void *parg)
{
while(1)
{
...........
喂狗
...........
}
}
答案:关中断、开中断
以下请问使用什么方式保护最为合适。
uint32 g=0;
void T1(void *parg)
{
while(1)
{
...........
g++;
...........
}
}
void T2(void *parg)
{
uint32_t a=0;
while(1)
{
...........
a = g;
...........
}
}
答案:自旋锁
以下请问使用什么方式保护最为合适。
void T1(void *parg)
{
while(1)
{
...........
printf("hello teacher.wen\r\n");
...........
}
}
void T2(void *parg)
{
while(1)
{
...........
printf("good bye teacher.wen\r\n");
...........
}
}
void T3(void *parg)
{
while(1)
{
...........
printf("he's teacher.wen\r\n");
...........
}
}
互斥锁
以下请问使用什么方式保护最为合适。
uint32 g=0;
void T1(void *parg)
{
while(1)
{
...........
g++;
...........
}
}
void T2(void *parg)
{
uint32_t a=0;
while(1)
{
...........
a = g;
...........
}
}
void USART1_IRQHandler(void)
{
...........
g = USART_ReceiveData(USART1);
...........
}
关中断、开中断(串口中断会打断T1和T2,串口中断本身耗时不长,可选择单独打断串口中断,保护g)
以下代码有啥问题?
void T1(void *parg)
{
while(1)
{
开互斥锁
printf("hello teacher.wen\r\n");
delay_ms(1000)
关互斥锁
}
}
void T2(void *parg)
{
while(1)
{
开互斥锁
printf("good bye teacher.wen\r\n");
delay_ms(1000)
关互斥锁
}
}
答:互斥应该快进快出,尽快归还CPU资源给其他线程,不能在互斥里使用不必要的延时。
以前把一整段代码一股脑的塞到互斥里面是多么低级的写法
开锁
……
一整段代码无脑塞
……
关锁