iOS 线程安全漫谈(一)
2017-12-14 本文已影响5人
yght
最近又在看osx线程安全一书,对线程同步和安全有了新的认识,所以还是想会写一篇文章
背景:最近针对项目内的核心的账户列表模块进行了优化,之前会在每次读区所有数据的时候都会读区整个文件iO,一下是优化之前CPU的消耗,可以看到平均值是在3.2ms.
2017-12-13 16:42:37.535504+0800 MyApp[44287:13446976] Load All accounts:140 using:0.005095005035400391
2017-12-13 16:42:37.977064+0800 MyApp[44287:13446336] Load All accounts:140 using:0.001426994800567627
在看看优化之后的消耗:平均值只有0.1ms!
2017-12-13 14:05:20.847531+0800 MyApp[39798:13238307] Finish Load all account with count:140 using:0.0001280307769775391
2017-12-13 14:05:20.857102+0800 MyApp[39798:13238307] Finish Load all account with count:140 using:0.000111997127532959
那么我是做了什么让读区性能时间提升了这么多?
首先为什么我会遇到这一问题,之前写了1个bug就是无意间循环调用了loadAllAccountItems()是千次,结果在iPad的真机上挂掉了,一查看内存长到480mb就被系统杀掉了。当然调用loadAllAccountItems()是的次数太多是又问题,但是就耽搁loadAllAccountItems()来说也是造成了不必要的时间和空间的浪费。
到这一步我们就可以分析出是主要是内存出了问题,没有共享,正常的思路是我们应该有一个数组来存储这块数据,但是这也有一个问题多线程访问情况下怎么处理呢,同时数据写入和读取肯定会照成Bad Access.但是简单的添加 @Synchronized又会造成纯读取情况下不必要的性能开销。另外,我们项目里面大多数情况还是需要的读取,所以读区性能的优化也额外重要。
最终,我运用的dispatch_barrier_XXX,解决了这一问题。这个是GCD提供的内存同步隔离手段,可以和GCD的并发队列一起实现高性能并行地读,同时线程安全的写。
具体代码如下:
并行读区:
dispatch_sync(self.concurrentQueue, ^{
allAccountDicts = self.accountDicArr.copy;
if (!allAccountDicts || allAccountDicts.count == 0) {
allAccountDicts = [FileManager LoadArrayFromDocument:[self accountsFileName] DirName:FileDataDir];
}
});
互斥写:
dispatch_barrier_sync(self.concurrentQueue, ^{
self.accountDicArr = accountsArray;
[FileManager SaveArrayToDocument:[NSArray arrayWithArray:accountsArray] FileName:[self accountsFileName] DirName:FileDataDir];
});
内存栅栏的功能原理图如下所示:
中间的2跟栅栏很形象地描绘了这一功能的作用:隔离(也就是说把容易出错的任务隔离出来,与其他的普通任务/Barrier 任务互斥)