NSThread
- show current Thread
atomic properties
- will frequently add lock for getter/setter methods, so low performance
Process VS Thread
Process
- a program in execution, can be thought as a instance of a program in execution
Thread
- exist within a process, sharing process's resource and memory space(including heap resource)
Difference between Thread and Process
- Process:
- Each process is executed in a separate address space
- One process cannot directly access the variables and data structure of another process
- Process have to use inter-process communication like files, pipe line, sockets to access another process's resource
Thread:
- Multiple threads within the same process will share the same heap space. This is vary different with process, which cannot directly access the memory of another process
Each thread still has its own registers and its own stack, but other threads can read and write the heap mempry
所有线程都具有相同的地址空间(进程的地址空间),线程可以访问该地址空间的每一个虚地址;
还可以访问进程所拥有的已打开文件、定时器、信号量等。
由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
线程共享的环境包括:
- 进程代码段
- 进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、
- 进程打开的文件描述符、信号的处理器、
- 进程的当前目录和进程用户ID与进程组ID。 .
Race condition
- when two or more threads can access shared data and they try to change it at the same time.
- avoid: add write lock to avoid race condition, such as NSLock, @synchronise link
Deadlock, conditions?
Deadlock
- two or more processes hold their own resource, and also wait for each other release their resource, this condition block the execution of process.
- In another word, two or more processes infinitely block and wait each other to release resource
Deallock necessary condition
- if deadlock occurs, these four must happen.
if one of four cannot meet, deadlock will not happend
mutual exclusion 互斥, 一个资源每次只能被一个进程使用
no preemption 不可抢占, 进程已获得资源,在未使用之前, 不能强行剥夺
hold and wait 占有并等待, 一个进程因请求资源而阻塞时,对已获得的资源保持不放
circular wait 环型等待,若干进程之前形成一种首尾相接的循环等待资源的关系
How to avoid Deadlock? add lock
atomic(just getter/setter)
mutax
@synchronized
func synchronized(_ lock: AnyObject, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
// 再结合 Swift 的尾随闭包的语⾔特性,这样,使⽤起来的时候就和 Objective-C 中很像了:
func myMethodLocked(anObj: AnyObject!) {
synchronized(anObj) {
// 在括号内 anObj 不会被其他线程改变
}
}
NSLock
DispatchQueue Barrier , can do for serial / concurrent queue
dispatch__semaphore__t
OSSpinLock
解除死锁的必要条件
不难看出,在死锁的四个必要条件中,第二、三和四项条件比较容易消除。通过引入事务机制,往往可以消除第二、三两项条件,方法是将所有上锁操作均作为事务对待,一旦开始上锁,即确保全部操作均可回退,同时通过锁管理器检测死锁,并剥夺资源(回退事务)。这种做法有时会造成较大开销,而且也需要对上锁模式进行较多改动。
消除第四项条件是比较容易且代价较低的办法。具体来说这种方法约定:上锁的顺序必须一致。具体来说,我们人为地给锁指定一种类似“水位”的方向性属性。无论已持有任何锁,该执行绪所有的上锁操作,必须按照一致的先后顺序从低到高(或从高到低)进行,且在一个系统中,只允许使用一种先后次序。
请注意,放锁的顺序并不会导致死锁。也就是说,尽管按照 锁A, 锁B, 放A, 放B 这样的顺序来进行锁操作看上去有些怪异,但是只要大家都按先A后B的顺序上锁,便不会导致死锁。
解决方法:
1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务; (细化处理逻辑,执行一段逻辑后便回滚或者提交,然后再执行其它逻辑,直到事物执行完毕提交)
2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
3 优化程序,检查并避免死锁现象出现;
4 .对所有的脚本和SP都要仔细测试,在正是版本之前。
5 所有的SP都要有错误处理(通过@error)
6 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁
另外参考的解决方法:
按同一顺序访问对象
如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。
避免事务中的用户交互
避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。
保持事务简短并在一个批处理中
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。
使用低隔离级别
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。
使用绑定连接
使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。