58.多线程编程对共享资源保护手段的使用

最近看了一些视频,看了下高手对多线程编程的操作,他们对共享资源保护手段使用的选择非常谨慎,结合以前的编程经历跟他们对比,才发现自己写的代码是多么糟糕。

这里仅做点心得总结,供自己日后回看,可能有不对的地方。

共享资源就是临界区,以前看注释总是看到临界区,就是不知道啥叫临界区~~

首先来段科普的,这是百度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资源给其他线程,不能在互斥里使用不必要的延时。
以前把一整段代码一股脑的塞到互斥里面是多么低级的写法
开锁
……
一整段代码无脑塞
……
关锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值