多线程和多核下“锁”的应用
2011-11-21 19:23
274 查看
假设这样一种情况:有多个线程(或多核)需要在共享数据A满足某一条件时,对A进行操作.
以下举例两种实现
Fun_1()
{ lock()--------------1.1
Result=Check(A)-----1.2
Unlock()------------1.3
-------------RISK!!!!
Lock()--------------1.4
Operate(A,result)---1.5
Unlock()------------1.6
}
Fun_2()
{ lock()--------------2.1
Result=Check(A)-----2.2
Operate(A,result)---2.3
Unlock()------------2.4
}
以上函数,只有fun_2 是安全的,FUN_1存在风险:
单核下:
当前线程完1.3 后如果发生了线程调度,那么数据a可能被其它线程修改。
重新回到1.4时,RESULT 已经不对了!!!
多核下:
当前线程完1.3 后如果发生了线程调度,那么数据a可能被其它线程修改。
重新回到1.4时,RESULT 已经不对了!!!
当前线程完1.3 后如果第二个核正好在运行代码1.5,那么数据a会被修改。
重新回到1.4时,RESULT 已经不对了!!!
***********************************************************************************************
锁的选择分为“信号量”“和自旋转锁”:
信号量会使程序休眠
自旋锁会关抢占,并进行忙等待
一般如果访问时间较短,或者使用多核处理器可以选择自旋锁
如果访问的时间较长(大于系统进行两次调度需要的时间)且是单核处理器,则可以选择信号量。
************************************************************************************************
linux下自旋锁函数:
spin_lock 使用情况:如果被保护的共享资源“只在进程上下文访问”
功能: 最基本的锁,没有对中断处理
spin_lock_bh 适用情况:如果被保护的共享资源“只在进程上下文和tasklet或timer上下文访问”
功能: spin_lock为基础,释放时开启上锁时禁止的软中断
spin_lock_irq 适用情况:如果被保护的共享资源“只在进程上下文和tasklet或timer上下文访问”
功能: spin_lock为基础,释放时开启中断
spin_lock_irqsave 适用情况:效率最差的一种,但当你不知道用哪个好时,可以用它
功能: spin_lock为基础,释放时恢复上锁时的中断状态
*******************************************************************************************************************
以下说明摘自网上:
如果被保护的共享资源“只在进程上下文访问和软中断上下文访问”,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。
当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快。
如果被保护的共享资源“只在进程上下文和tasklet或timer上下文访问”,那么应该使用与上面情况相同的获得和释放锁的宏,因为tasklet和timer是用软中断实现的。
如果被保护的共享资源“只在一个tasklet或timer上下文访问”,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。
timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。
如果被保护的共享资源“只在两个或多个tasklet或timer上下文”访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。
如果被保护的共享资源“只在一个软中断(tasklet和timer除外)上下文访问”,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。
如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
如果被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
而在中断处理句柄中使用什么版本,需依情况而定,如果只有一个中断处理句柄访问该共享资源,那么在中断处理句柄中仅需要spin_lock和spin_unlock来保护对共享资源的访问就可以了。
因为在执行中断处理句柄期间,不可能被同一CPU上的软中断或进程打断。但是如果有不同的中断处理句柄访问该共享资源,那么需要在中断处理句柄中使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
在使用spin_lock_irq和spin_unlock_irq的情况下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些。
因为它比spin_lock_irqsave要快一些,但是如果你不能确定是否中断使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。
当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用spin_lock_irq和spin_unlock_irq最好。
以下举例两种实现
Fun_1()
{ lock()--------------1.1
Result=Check(A)-----1.2
Unlock()------------1.3
-------------RISK!!!!
Lock()--------------1.4
Operate(A,result)---1.5
Unlock()------------1.6
}
Fun_2()
{ lock()--------------2.1
Result=Check(A)-----2.2
Operate(A,result)---2.3
Unlock()------------2.4
}
以上函数,只有fun_2 是安全的,FUN_1存在风险:
单核下:
当前线程完1.3 后如果发生了线程调度,那么数据a可能被其它线程修改。
重新回到1.4时,RESULT 已经不对了!!!
多核下:
当前线程完1.3 后如果发生了线程调度,那么数据a可能被其它线程修改。
重新回到1.4时,RESULT 已经不对了!!!
当前线程完1.3 后如果第二个核正好在运行代码1.5,那么数据a会被修改。
重新回到1.4时,RESULT 已经不对了!!!
***********************************************************************************************
锁的选择分为“信号量”“和自旋转锁”:
信号量会使程序休眠
自旋锁会关抢占,并进行忙等待
一般如果访问时间较短,或者使用多核处理器可以选择自旋锁
如果访问的时间较长(大于系统进行两次调度需要的时间)且是单核处理器,则可以选择信号量。
************************************************************************************************
linux下自旋锁函数:
spin_lock 使用情况:如果被保护的共享资源“只在进程上下文访问”
功能: 最基本的锁,没有对中断处理
spin_lock_bh 适用情况:如果被保护的共享资源“只在进程上下文和tasklet或timer上下文访问”
功能: spin_lock为基础,释放时开启上锁时禁止的软中断
spin_lock_irq 适用情况:如果被保护的共享资源“只在进程上下文和tasklet或timer上下文访问”
功能: spin_lock为基础,释放时开启中断
spin_lock_irqsave 适用情况:效率最差的一种,但当你不知道用哪个好时,可以用它
功能: spin_lock为基础,释放时恢复上锁时的中断状态
*******************************************************************************************************************
以下说明摘自网上:
如果被保护的共享资源“只在进程上下文访问和软中断上下文访问”,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。
当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和spin_unlock_bh是最恰当的,它比其他两个快。
如果被保护的共享资源“只在进程上下文和tasklet或timer上下文访问”,那么应该使用与上面情况相同的获得和释放锁的宏,因为tasklet和timer是用软中断实现的。
如果被保护的共享资源“只在一个tasklet或timer上下文访问”,那么不需要任何自旋锁保护,因为同一个tasklet或timer只能在一个CPU上运行,即使是在SMP环境下也是如此。实际上tasklet在调用tasklet_schedule标记其需要被调度时已经把该tasklet绑定到当前CPU,因此同一个tasklet决不可能同时在其他CPU上运行。
timer也是在其被使用add_timer添加到timer队列中时已经被帮定到当前CPU,所以同一个timer绝不可能运行在其他CPU上。当然同一个tasklet有两个实例同时运行在同一个CPU就更不可能了。
如果被保护的共享资源“只在两个或多个tasklet或timer上下文”访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。
如果被保护的共享资源“只在一个软中断(tasklet和timer除外)上下文访问”,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。
如果被保护的共享资源在两个或多个软中断上下文访问,那么这个共享资源当然更需要用spin_lock和spin_unlock来保护,不同的软中断能够同时在不同的CPU上运行。
如果被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
而在中断处理句柄中使用什么版本,需依情况而定,如果只有一个中断处理句柄访问该共享资源,那么在中断处理句柄中仅需要spin_lock和spin_unlock来保护对共享资源的访问就可以了。
因为在执行中断处理句柄期间,不可能被同一CPU上的软中断或进程打断。但是如果有不同的中断处理句柄访问该共享资源,那么需要在中断处理句柄中使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
在使用spin_lock_irq和spin_unlock_irq的情况下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些。
因为它比spin_lock_irqsave要快一些,但是如果你不能确定是否中断使能,那么使用spin_lock_irqsave和spin_unlock_irqrestore更好,因为它将恢复访问共享资源前的中断标志而不是直接使能中断。
当然,有些情况下需要在访问共享资源时必须中断失效,而访问完后必须中断使能,这样的情形使用spin_lock_irq和spin_unlock_irq最好。
相关文章推荐
- Android 应用性能优化(三) 多核下的多线程计算
- 【多线程】应用Java多线程实例
- Linux 多线程应用中如何编写安全的信号处理函数
- Java多线程与并发应用-(9)-锁lock+条件阻塞conditon实现线程同步通信
- SQLite在多线程环境下的应用,threadsafe
- 多线程(关于售票的简单应用)
- JAVA多线程 join() 方法详解及应用场景
- Android 多线程之 Handler、Looper、Message 在基于 HTTP 系统中的应用
- java多线程有哪些实际的应用场景?
- 多线程和应用
- 多线程中的Future模式及其在高性能IO框架netty中的应用
- dc在多线程的应用
- 多线程和并发库应用六-ThreadLocal
- 多线程和并发库应用八-线程池
- Java 多线程应用 之 ArrayBlockingQueue
- C++笔记之多线程的理解与应用
- 多线程应用方法(上)
- Linux 多线程应用中如何编写安全的信号处理函数
- ProgressBar控件在Listview下的多线程应用
- Java多线程学习与Java多线程的简单应用