完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1.自旋锁
自旋锁可以看做一个资源二值标识量,当在使用资源前,需要检测锁是否锁上,如果锁已经被锁上,那么就在当前位置不停地检测锁的状态,直到锁被解锁为止。如果锁没有锁没有锁上,那么就将锁锁上,然后再进入临界资源,当临界资源访问结束后,再将其解锁。其示意代码如下: /* 初始化*Spin为真 */ SPIN_TYPE MySpin = true; void SpinLock(char *Spin) { while(*Spin==false) /* 检测操作为原子操作,且跳转操作不受其他线程影响 */ ; *Spin = 0; /* 上锁操作为原子操作 */ } void SpinUnLock(char *Spin) { *Spin = 1; /* 解锁操作为原子操作 */ } 考虑到atmega128中没有对操作系统支持的指令,为了在atmega128中实现以上的操作条件,这里只能使用开关中断来实现原子操作,但是开关中断的设置需要一定的技巧,尽量减少关中断的时间。参考atmega128指令集,这里给出WinAVR下自旋锁的实现代码: char MySpin = 1; /* 初始化自旋锁 */ void SpinLock(char *Spin) { asm volatile("cli "); /* 关中断 */ loop: if((*Spin)==0) { asm volatile("sei"); /* 开中断 */ goto loop; /* 跳转达到循环检测 */ } (*Spin) = 0; /* 上锁操作 */ asm volatile("sei"); } void SpinUnLock(char *Spin) { asm volatile("cli "); /* 关中断 */ (*Spin) = 1; /* 解锁操作 */ asm volatile("sei"); /* 开中断 */ } 上面的上锁操作为了达到原子性,使用了goto语句,注意,在循环检测前必须打开中断,否则调度器会被锁住,导致无法进行任务调度。这里可能会有读者问,为什么在goto loop前可以开中断,那样不就破坏了原子性吗?其实不然,我们可以假设当在goto loop前调度器恰好切换到了其他线程,那么此时由于下一条指令是jump 指令,由于在atmega128中,跳转指令只与状态寄存器中的标志位和跳转地址有关,所以在线程切换回来时,由于线程的现场环境得到恢复,其他线程不会对跳转产生影响,所以该方法可以实现上锁操作。 以上实现较为麻烦的原因是atmega128没有提供对操作系统支持的指令,只有LOAD和STR指令,从RAM中读取检测并写回的操作实际上会有4条左右的指令,显然在访问公共变量Spin时,会出现错乱。如果atmega128有像TAS,SWAP等这类指令,那么就可以很容易的实现自旋锁、信号量等线程同步组件。其实相对atmega128而言,51单片机更容易实现自旋锁和信号量,这是因为在51单片机中存在RAM与RAM之间直接操作的指令,一条指令即可实现RAM与RAM之间的数据交换,而且跳转指令中提供了直接根据RAM中数据条件来进行跳转的指令,这是非常有利于实现自旋锁和信号量。 2.自旋锁的缺陷 自旋锁虽然简单,但是自旋锁有一个很致命的缺陷,就是当一个线程访问临界资源太久时,另一个线程只能干等,无限的忙等待,这显然会占用CPU的资源,而且做了很长时间的“无用功”。为了解决这个问题,最简单的方法是在发现锁已经被锁住时,直接将自己挂起,然后当另一个线程使用完临界资源后,再将挂起的线程唤醒。这样就节省了等待时间。同时极大减小了CPU的线程切换开销。 自旋锁一般用在临界资源访问时间很短的情况下,在这种情况下,自旋锁比二值信号量的响应比更小。所以在具体的实际应用中,应该根据具体要求合理选择,没有哪个方法是通用的。 3.atmega128自旋锁实验 以下是关键代码: void task1(void) { while(1) { SpinLock(&MySpin); USART_SendString("Task1 is running!"); SpinUnLock(&MySpin); delay(3); } } void task2(void) //任务函数2 { while(1) { SpinLock(&MySpin); USART_SendString("Task2 is running!"); SpinUnLock(&MySpin); delay(3); } } void task3(void) //任务函数2 { while(1) { SpinLock(&MySpin); USART_SendString("Task3 is running!"); SpinUnLock(&MySpin); delay(3); } } 以下是运行效果: 当我们不使用自旋锁时,结果如下: 从控制台上可以看出,串口打印出了太多的错误,这显然是临界资源访问冲突导致的,即以串口为临界资源的访问冲突。 使用自旋锁的结果: 从上面可以看出,自旋锁有效地解决了临界资源的访问冲突问题。 |
|
|
|
只有小组成员才能发言,加入小组>>
3269 浏览 9 评论
2947 浏览 16 评论
3446 浏览 1 评论
8967 浏览 16 评论
4041 浏览 18 评论
1084浏览 3评论
562浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
556浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2294浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1850浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-13 10:39 , Processed in 0.815723 second(s), Total 45, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号