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
时间: 2024-10-13 16:15:30