原创Blog,转载请注明出处
http://blog.csdn.net/hello_hwc?viewmode=list
我的stackoverflow
前言:最近app中的日历小概率的在currentCalendar这个方法崩溃,看了下call
tree。研究了下,是线程安全问题。这里,就系统性的总结下线程安全这部分。
之后的博客绝大部分源码会用Swift来写了。
一些资料
iOS中的线程
什么是线程?
线程是操作系统进行调度的最小单位,它被包含在进程中,是进程运作的实际单位。在单核系统中,按照时间片轮转的方式实现线程的并发执行,在多核系统中,能够真正实现线程同时执行。
iOS中与多线程相关的常用的有NSThread,GCD,NSOperationQueue.还有一个不常用,但是理解很重要的NSRunloop。
什么是线程安全?
当一段代码被多个线程执行,执行后的结果和多个线程依次执行后的结果一致,那么这段代码就是线程安全的。
再看看WIKI中的定义
A piece of code is thread-safe if it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time.
一段代码在多个线程上调用,并且共享一段内存空间。如果代码能够安全执行,就是线程安全的。
举个例子
NSMutableArray不是线程安全的,那么以下代码就会小概率崩溃
let queue1 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_SERIAL)
let queue2 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_SERIAL)
dispatch_async(queue1) { () -> Void in
for index in 1...500{
self.emptyArray.addObject(NSNumber(integer: index))
}
}
dispatch_async(queue2) { () -> Void in
for index in 500...1000{
self.emptyArray.addObject(NSNumber(integer: index))
}
}
运行的话,崩溃如图
错误Log
malloc: * error for object 0x7fb5804a9d40: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
注意,这种崩溃是小概率的,但是当用户量大的时候,就会知道小概率也是不可忽视的
这不会详细介绍NSMutableArray的背后实现原理,感兴趣的同学可以看看这篇文章
错误原因-抽象到malloc和free的问题
每次malloc会分配新的地址,然后free释放。有可能按照如图的方式malloc-malloc-free-free.这样同一个地址会free两次,也就crash了。类似的在ARC中release一个reference count为0的对象也会出错。
UIKit以及Fundation
事实上,大多数Cocoa提供的Api都不是线程安全的,尤其是与UI相关的UIKit,只能在主线程上操作。
在后台更新UI是很多开发者容易犯的错误
那么,为什么不把Cocoa的API写成线程安全的呢?Apple那么多天才工程师,难道解决不了吗?
答案很明显:为了执行效率,大多数的时候不需要并行的执行一段代码,而加上锁,递归锁之类的东西,执行效率会降低很多。需要线程安全的时候,开发者自己维护就可以了。
通常,不可变对象是线程安全的,可变对象不是线程安全的
以下Fundation对象是线程安全的,
意味着,你可以在多个线程中访问一个对象(读写),而不需要加锁
NSArray
NSAssertionHandler
NSAttributedString
NSCalendarDate
NSCharacterSet
NSConditionLock
NSConnection
NSDat