iOS开发-线程安全-09-多线程

  1 返回主页
  2 GarveyCalvin
  3 
  4 程序人生-改变未来
  5 
  6 博客园
  7 首页
  8 新随笔
  9 联系
 10 订阅
 11 管理
 12 随笔- 29  文章- 29  评论- 43 
 13 iOS开发-多线程开发之线程安全篇
 14 
 15 前言:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件和同一个方法等。因此当多个线程访问同一块资源时,很容易会发生数据错误及数据不安全等问题。因此要避免这些问题,我们需要使用“线程锁”来实现。
 16 
 17  
 18 
 19 本文主要论述IOS创建锁的方法总结,如果大家对多线程编程技术这一块不熟悉,我建议你们先去看我的另一篇文章”iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)“
 20 
 21  
 22 
 23 一、使用关键字
 24 
 25 1)@synchronized(互斥锁)
 26 
 27 优点:使用@synchronized关键字可以很方便地创建锁对象,而且不用显式的创建锁对象。
 28 
 29 缺点:会隐式添加一个异常处理来保护代码,该异常处理会在异常抛出的时候自动释放互斥锁。而这种隐式的异常处理会带来系统的额外开销,为优化资源,你可以使用锁对象。
 30 
 31 二、“Object-C”语言
 32 
 33 1)NSLock(互斥锁)
 34 
 35 2)NSRecursiveLock(递归锁)
 36 
 37 条件锁,递归或循环方法时使用此方法实现锁,可避免死锁等问题。
 38 
 39 3)NSConditionLock(条件锁)
 40 
 41 使用此方法可以指定,只有满足条件的时候才可以解锁。
 42 
 43 4)NSDistributedLock(分布式锁)
 44 
 45 在IOS中不需要用到,也没有这个方法,因此本文不作介绍,这里写出来只是想让大家知道有这个锁存在。
 46 
 47 如果想要学习NSDistributedLock的话,你可以创建MAC OS的项目自己演练,方法请自行Google,谢谢。
 48 
 49 三、C语言
 50 
 51 1)pthread_mutex_t(互斥锁)
 52 
 53 2)GCD-信号量(“互斥锁”)
 54 
 55 3)pthread_cond_t(条件锁)
 56 
 57  
 58 
 59 线程安全 —— 锁
 60 
 61 一、使用关键字:
 62 
 63 1)@synchronized
 64 
 65 复制代码
 66 // 实例类person
 67 Person *person = [[Person alloc] init];
 68 
 69 // 线程A
 70 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 71     @synchronized(person) {
 72         [person personA];
 73         [NSThread sleepForTimeInterval:3]; // 线程休眠3秒
 74     }
 75 });
 76 
 77 // 线程B
 78 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 79     @synchronized(person) {
 80         [person personB];
 81     }
 82 });
 83 复制代码
 84 关键字@synchronized的使用,锁定的对象为锁的唯一标识,只有标识相同时,才满足互斥。如果线程B锁对象person改为self或其它标识,那么线程B将不会被阻塞。你是否看到@synchronized(self) ,也是对的。它可以锁任何对象,描述为@synchronized(anObj)。
 85 
 86  
 87 
 88 二、Object-C语言
 89 
 90 1)使用NSLock实现锁
 91 
 92 复制代码
 93 // 实例类person
 94 Person *person = [[Person alloc] init];
 95 // 创建锁
 96 NSLock *myLock = [[NSLock alloc] init];
 97 
 98 // 线程A
 99 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
100     [myLock lock];
101     [person personA];
102     [NSThread sleepForTimeInterval:5];
103     [myLock unlock];
104 });
105 
106 // 线程B
107 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
108     [myLock lock];
109     [person personB];
110     [myLock unlock];
111 }); 
112 复制代码
113 程序运行结果:线程B会等待线程A解锁后,才会去执行线程B。如果线程B把lock和unlock方法去掉之后,则线程B不会被阻塞,这个和synchronized的一样,需要使用同样的锁对象才会互斥。
114 
115 NSLock类还提供tryLock方法,意思是尝试锁定,当锁定失败时,不会阻塞进程,而是会返回NO。你也可以使用lockBeforeDate:方法,意思是在指定时间之前尝试锁定,如果在指定时间前都不能锁定,也是会返回NO。
116 
117 注意:锁定(lock)和解锁(unLock)必须配对使用
118 
119  
120 
121 2)使用NSRecursiveLock类实现锁 
122 
123 复制代码
124 // 实例类person
125 Person *person = [[Person alloc] init];
126 // 创建锁对象
127 NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
128 
129 // 创建递归方法
130 static void (^testCode)(int);
131 testCode = ^(int value) {
132     [theLock tryLock];
133     if (value > 0)
134     {
135         [person personA];
136         [NSThread sleepForTimeInterval:1];
137         testCode(value - 1);
138     }
139     [theLock unlock];
140 };
141 
142 //线程A
143 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
144     testCode(5);
145 });
146 
147 //线程B
148 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
149     [theLock lock];
150     [person personB];
151     [theLock unlock];
152 });
153 复制代码
154 如果我们把NSRecursiveLock类换成NSLock类,那么程序就会死锁。因为在此例子中,递归方法会造成锁被多次锁定(Lock),所以自己也被阻塞了。而使用NSRecursiveLock类,则可以避免这个问题。
155 
156  
157 
158 3)使用NSConditionLock(条件锁)类实现锁:
159 
160 使用此方法可以创建一个指定开锁的条件,只有满足条件,才能开锁。
161 
162 复制代码
163 // 实例类person
164 Person *person = [[Person alloc] init];
165 // 创建条件锁
166 NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
167 
168 // 线程A
169 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
170     [conditionLock lock];
171     [person personA];
172     [NSThread sleepForTimeInterval:5];
173     [conditionLock unlockWithCondition:10];
174 });
175 
176 // 线程B
177 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
178     [conditionLock lockWhenCondition:10];
179     [person personB];
180     [conditionLock unlock];
181 });
182 复制代码
183 线程A使用的是lock方法,因此会直接进行锁定,并且指定了只有满足10的情况下,才能成功解锁。
184 
185 unlockWithCondition:方法,创建条件锁,参数传入“整型”。lockWhenCondition:方法,则为解锁,也是传入一个“整型”的参数。
186 
187  
188 
189 三、C语言
190 
191 1)使用pthread_mutex_t实现锁
192 
193 注意:必须在头文件导入:#import <pthread.h>
194 
195 复制代码
196 // 实例类person
197 Person *person = [[Person alloc] init];
198 
199 // 创建锁对象
200 __block pthread_mutex_t mutex;
201 pthread_mutex_init(&mutex, NULL);
202 
203 // 线程A
204 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
205     pthread_mutex_lock(&mutex);
206     [person personA];
207     [NSThread sleepForTimeInterval:5];
208     pthread_mutex_unlock(&mutex);
209 });
210 
211 // 线程B
212 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
213     pthread_mutex_lock(&mutex);
214     [person personB];
215     pthread_mutex_unlock(&mutex);
216 });
217 复制代码
218 实现效果和上例的相一致
219 
220   
221 
222 2)使用GCD实现“锁”(信号量)
223 
224 GCD提供一种信号的机制,使用它我们可以创建“锁”(信号量和锁是有区别的,具体请自行百度)。
225 
226 复制代码
227 // 实例类person
228 Person *person = [[Person alloc] init];
229 
230 // 创建并设置信量
231 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
232 
233 // 线程A
234 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
235     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
236     [person personA];
237     [NSThread sleepForTimeInterval:5];
238     dispatch_semaphore_signal(semaphore);
239 });
240 
241 // 线程B
242 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
243     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
244     [person personB];
245     dispatch_semaphore_signal(semaphore);
246 });
247 复制代码
248 效果也是和上例介绍的相一致。
249 
250 我在这里解释一下代码。dispatch_semaphore_wait方法是把信号量加1,dispatch_semaphore_signal是把信号量减1。
251 
252 我们把信号量当作是一个计数器,当计数器是一个非负整数时,所有通过它的线程都应该把这个整数减1。如果计数器大于0,那么则允许访问,并把计数器减1。如果为0,则访问被禁止,所有通过它的线程都处于等待的状态。
253 
254  
255 
256 3)使用POSIX(条件锁)创建锁
257 
258 复制代码
259 // 实例类person
260 Person *person = [[Person alloc] init];
261 
262 // 创建互斥锁
263 __block pthread_mutex_t mutex;
264 pthread_mutex_init(&mutex, NULL);
265 // 创建条件锁
266 __block pthread_cond_t cond;
267 pthread_cond_init(&cond, NULL);
268 
269 // 线程A
270 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
271     pthread_mutex_lock(&mutex);
272     pthread_cond_wait(&cond, &mutex);
273     [person personA];
274     pthread_mutex_unlock(&mutex);
275 });
276 
277 // 线程B
278 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
279     pthread_mutex_lock(&mutex);
280     [person personB];
281     [NSThread sleepForTimeInterval:5];
282     pthread_cond_signal(&cond);
283     pthread_mutex_unlock(&mutex);
284 });
285 复制代码
286 效果:程序会首先调用线程B,在5秒后再调用线程A。因为在线程A中创建了等待条件锁,线程B有激活锁,只有当线程B执行完后会激活线程A。
287 
288 pthread_cond_wait方法为等待条件锁。
289 
290 pthread_cond_signal方法为激动一个相同条件的条件锁。
291 
292  
293 
294  
295 
296 简单总结:
297 
298 一般来说,如果项目不大,我们都会偷点懒,直接使用关键字@synchronized建立锁,懒人方法。其次可以使用苹果提供的OC方法,最后才会去使用C去建立锁。
299 
300  
301 
302  
303 
304  
305 
306  
307 
308 本文参考文章:
309 
310 iOS多线程开发(四)---线程同步
311 
312 Objective-C中不同方式实现锁(一)
313 
314 信号量与互斥锁
315 
316  
317 
318  
319 
320  
321 
322 博文作者:GarveyCalvin
323 
324 博文出处:http://www.cnblogs.com/GarveyCalvin/
325 
326 本文版权归作者和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作!
327 
328  
329 
330 笔者需要换工作,工作地点:广州,职位:ios开发工程师,如有介绍的请推荐一下,谢谢。
331 分类: IOS开发-Object C, 多线程
332 标签: IOS, OBJECT-C, 手机开发, 多线程, 互斥锁, 递归锁, 条件锁, 线程安全
333 绿色通道: 好文要顶 关注我 收藏该文与我联系 
334 
335 GarveyCalvin
336 关注 - 5
337 粉丝 - 35
338 +加关注
339 0
340 0
341 (请您对文章做出评价)
342 « 上一篇:Git-学习笔记(常用命令集合)
343 » 下一篇:MAC-Zsh安装与使用——终极Shell
344 posted @ 2015-02-10 14:15 GarveyCalvin 阅读(405) 评论(0) 编辑 收藏
345 刷新评论刷新页面返回顶部
346 注册用户登录后才能发表评论,请 登录 或 注册,访问网站首页。
347 【免费课程】案例:导航条菜单的制作
348 【推荐】50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
349 融云,免费为你的App加入IM功能——让你的App“聊”起来!!
350 
351 最新IT新闻:
352 · 小米或于本月内推出超低价手机:399元红米
353 · 封闭还是开放?Android Wear离iOS究竟有多远
354 · 平板手机用户最爱看什么?数据统计给出的答案是体育
355 · Feynman Liang:一年修完50堂MOOC课程的修课狂人
356 · 独立局部主义精神
357 » 更多新闻...
358 
359 最新知识库文章:
360 · 图片服务架构演进
361 · 软件架构师是一个角色,不是一项工作
362 · 给公司部门设计的SOA架构
363 · 好代码不值钱
364 · 关于响应式布局
365 » 更多知识库文章...
366 公告
367 
368 昵称:GarveyCalvin
369 园龄:4个月
370 粉丝:35
371 关注:5
372 +加关注
373 <    2015年3月    >
374 日    一    二    三    四    五    六
375 22    23    24    25    26    27    28
376 1    2    3    4    5    6    7
377 8    9    10    11    12    13    14
378 15    16    17    18    19    20    21
379 22    23    24    25    26    27    28
380 29    30    31    1    2    3    4
381 搜索
382 
383  
384  
385 常用链接
386 
387 我的随笔
388 我的评论
389 我的参与
390 最新评论
391 我的标签
392 更多链接
393 随笔分类
394 
395 C语言
396 IOS开发-Object C(20)
397 IOS开发-Swift(3)
398 MySQL-数据库(2)
399 Quartz2D(1)
400 动画效果(3)
401 多线程(2)
402 其它(7)
403 自动布局(2)
404 随笔档案
405 
406 2015年3月 (1)
407 2015年2月 (4)
408 2015年1月 (5)
409 2014年12月 (13)
410 2014年11月 (5)
411 2014年10月 (1)
412 文章分类
413 
414 apple开发者账户
415 Git使用(1)
416 IOS类别(6)
417 MAC类别(2)
418 科学上网(1)
419 数据库
420 英文学习(2)
421 转载
422 最新评论
423 
424 1. Re:MySQL之终端(Terminal)管理数据库、数据表、数据的基本操作
425 收藏了 楼主辛苦了
426 --mesoar
427 2. Re:iOS开发-正则表达式的使用方法
428 @董铂然谢谢,目前我也是使用过一小部分的元字符。...
429 --GarveyCalvin
430 3. Re:iOS开发-正则表达式的使用方法
431 写的不错 但我只用过 .*?和(.*?)。
432 --董铂然
433 阅读排行榜
434 
435 1. MySQL之终端(Terminal)管理数据库、数据表、数据的基本操作(825)
436 2. iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)(751)
437 3. iOS开发-UIView之动画效果的实现方法(合集)(741)
438 评论排行榜
439 
440 1. iOS开发-项目的完整重命名方法,图文教程。(11)
441 2. Git-学习笔记(常用命令集合)(7)
442 3. iOS开发-自动布局之autoresizingMask使用详解(Storyboard&Code)(6)
443 推荐排行榜
444 
445 1. Git-学习笔记(常用命令集合)(8)
446 2. iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)(4)
447 3. iOS开发-Object-C Block的实现方式(4)
448 Copyright ©2015 GarveyCalvin

 

posted on 2016-04-20 23:30  爱你久久iOS  阅读(194)  评论(0编辑  收藏  举报

导航