先来看一下代码:

- (void)viewDidLoad {
[super viewDidLoad]; self.testStr = @"String initial complete";
[self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, ), ^{
NSLog(@"before sleep %@",self.testStr);
sleep();
NSLog(@"after sleep %@",self.testStr);
});
}
- (void)changeStr{ self.testStr = @"String has changed";
}

执行结果:

-- ::40.525 线程安全测试[:] before sleep String initial complete
-- ::43.598 线程安全测试[:] after sleep String has changed

会发现在异步执行中如果testStr改变了,那么异步线程里的testStr也会改变这样就没法保证异步对资源独占操作

如果在异步block里创建一个str赋值如下代码:

- (void)viewDidLoad {
[super viewDidLoad]; self.testStr = @"String initial complete";
[self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, ), ^{
NSString *str = self.testStr;
NSLog(@"before sleep %@",str);
sleep();
NSLog(@"after sleep %@",str);
});
}
- (void)changeStr{ self.testStr = @"String has changed";
}

执行结果:

-- ::09.785 线程安全测试[:] before sleep String initial complete
-- ::12.786 线程安全测试[:] after sleep String initial complete

这样新的string就不会受到外部改变的影响,但是如果在这个赋值时刻self.asStr已变成野指针那么后面的操作还是会出错,虽然这样情况不是那么容易出现。

如何防止这种情况呢,可以看看下面的代码:

- (void)viewDidLoad {
[super viewDidLoad]; self.testStr = @"String initial complete";
[self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
__weak __typeof(self.testStr) weakString = self.testStr;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, ), ^{
__strong __typeof(weakString) strongString = weakString;
if(strongString) {
NSLog(@"before sleep %@",strongString);
sleep();
NSLog(@"after sleep %@",strongString);
}
});
}
- (void)changeStr{ self.testStr = @"String has changed";
}

执行结果:

-- ::40.320 线程安全测试[:] before sleep String initial complete
-- ::43.388 线程安全测试[:] after sleep String initial complete

weakString会在self.asStr释放时置为nil,如果不是nil时,能够确保对象在block调用的完整周期里面被retain,如果被抢占对strongString的执行会继续并且会产生一样的值,如果strongString执行到时是nil,那么block不能正确执行前已经返回,这样就不会出现先前那样的问题。

还可以用锁来保证多个线程对一份资源在操作时不会被更改

#import "ViewController.h"
#include <pthread.h>
@interface ViewController ()
{
pthread_mutex_t _mutex;
}
@property (nonatomic, copy)NSString *testStr;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; pthread_mutex_init(&_mutex, NULL);
self.testStr = @"String initial complete";
[self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, ), ^{
pthread_mutex_lock(&_mutex);
NSLog(@"before sleep %@",self.testStr);
sleep();
NSLog(@"after sleep %@",self.testStr);
pthread_mutex_unlock(&_mutex);
});
}
- (void)changeStr{ pthread_mutex_lock(&_mutex);
self.testStr = @"string has changed";
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
}
@end

执行结果:

-- ::57.194 线程安全测试[:] before sleep String initial complete
-- ::00.269 线程安全测试[:] after sleep String initial complete

在RAC中使用的是OSSpinLock来保证线程安全的,不过几位苹果工程师在swift-dev邮件列表中讨论weak属性的线程安全问题的邮件里爆出自旋锁有bug,邮件地址:https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html。大概就是不同优先级线程调度算法会有优先级反转问题,比如低优先级获锁访问资源,高优先级尝试访问时会等待,这时低优先级又没法争过高优先级导致任务无法完成lock释放不了。也可以看看ReactiveCo社区的讨论https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2619

本来OSSpinLock是性能最高的锁,但是由于如果不在同一个优先级线程进行锁操作就不能保证安全,那么dispatch_semaphore和pthread_mutex这种仅次于自旋锁的可以作为替代方案。我注意到facebook的KVOController在2016年5月17日时的一个Commit里将所有OSSpinLock替换成了pthread_mutex,可参看这个commithttps://github.com/facebook/KVOController/commit/4f5c329b26f48b151eed82da085288763e2e1761。pthread_mutex会在新系统中性能得到很大的提升,所以可以考虑这个方案。

iOS开发线程安全问题的更多相关文章

  1. iOS 开发线程 gcd

      基础知识: 下午9:09 一.基础概念 1.什么是GCD 全称是Grand Central Dispath 纯C语言编写,提供非常多且强大的函数,是目前推荐的多线程开发方法,NSOperation ...

  2. iOS开发 - 线程与进程的认识与理解

    进程: 进程是指在系统中正在运行的一个应用程序,比如同时打开微信和Xcode,系统会分别启动2个进程; 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内; 线程: 一个进程要想执行任务 ...

  3. iOS开发--线程通信

    线程间的通信主要用于主线程与子线程的,也有用于子线程与子线程的 介绍下面几种通信方式 1.利用GCD方式(推荐) - (void)touchesBegan:(NSSet<UITouch *> ...

  4. iOS开发线程同步技术-锁

    概览 1,什么是锁(临界区)? 2,常用的锁有哪些? 3,相关链接 什么是锁(临界区) 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法. 常用的锁有哪些? 互斥锁:是一种用于多线程编 ...

  5. iOS开发多线程篇—线程安全

    iOS开发多线程篇—线程安全 一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块 ...

  6. iOS开发Swift篇(02) NSThread线程相关简单说明

    iOS开发Swift篇(02) NSThread线程相关简单说明 一 说明 1)关于多线程部分的理论知识和OC实现,在之前的博文中已经写明,所以这里不再说明. 2)该文仅仅简单讲解NSThread在s ...

  7. 李洪强iOS开发Swift篇---12_NSThread线程相关简单说明

    李洪强iOS开发Swift篇---12_NSThread线程相关简单说明 一 说明 1)关于多线程部分的理论知识和OC实现,在之前的博文中已经写明,所以这里不再说明. 2)该文仅仅简单讲解NSThre ...

  8. iOS开发多线程篇 03 —线程安全

    iOS开发多线程篇—线程安全 一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块 ...

  9. iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用

    目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 多线程的四种解决方案:pthread,NSThread,GCD,N ...

随机推荐

  1. WINDOWS 下设置单独的java环境

    set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_65set PATH=%JAVA_HOME%\bin;%PATH%;D:cd D:\JavaProjectcm ...

  2. Android中长度单位和边距

    Android表示单位长度的方式通常有三种表示方式. 距离单位☞px:表示屏幕实际的象素.例如,320*480的屏幕在横向有320个象素,在纵向有480个象素 距离单位☞dp:dp = dpi     ...

  3. Unity手游之路手游代码更新策略探讨

    版权声明: https://blog.csdn.net/janeky/article/details/25923151 这几个月公司项目非常忙.加上家里事情也多,所以blog更新一直搁置了. 近期在项 ...

  4. MongoDB基础教程系列--目录结构

    最近对 MongoDB 产生兴趣,在网上找的大部分都是 2.X 版本,由于 2.X 与 3.X 差别还是很大的,所以自己参考官网,写了本系列.MongoDB 的知识还是很多的,本系列会持续更新,本文作 ...

  5. SSM后台管理系统(Spring SpringMVC Mybatis Mysql EasyUI)

    非常简单的一个后台管理系统,功能不多,框架也不复杂, 源码下载(附数据库)-ssm后台管理系统框架(Spring mvc + mybatis + mysql + easyui ) 实例图片

  6. 【转】同一个SQL查询语句,为什么使用到的索引不同?

    问: 同一个SQL查询语句,只是修改where条件中的一个值,为什么使用到的索引情况也会不同?谢谢! 1) explain执行结果,如下图: 2) 表中的数据如下图: 3) 表结构如下图: 4) 创建 ...

  7. nginx跟apache访问方法

    ifconfig 在浏览器中输入ip即可访问 centos安装nginx环境 1:进入 cd /usr/local/src  //下载文件放到这个目录中 2:wget http://nginx.org ...

  8. metamask中的import account的代码实现

    metamask-extension/app/scripts/account-import-strategies/index.js 这部分就是用户如果往metamask中import一个已有的账户调用 ...

  9. 手把手教你Chrome浏览器安装Postman(含下载云盘链接)(转)

    文章转自http://www.ljwit.com/archives/php/278.html 说明: Postman不多介绍,是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件.本文主要 ...

  10. mysql索引类型-方法-形式-使用时机-不足之处--注意事项

    一.索引的类型 1.普通索引   增加 create  index  index_name on table(colume(length));                       例子:cre ...