先来看一下代码:

- (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. 理解LSTM

    本文基于Understanding-LSTMs进行概括整理,对LSTM进行一个简单的介绍 什么是LSTM LSTM(Long Short Term Memory networks)可以解决传统RNN的 ...

  2. 基于canvas图像处理的图片 灰色图像

    图片展示网页往往色彩繁杂,当一个网页上有多张图片的时候用户的注意力就很不容易集中,而且会造成网站整个色调风格的不可把控. 能不能把所有的预览图变成灰度图片,等用户激活某张图片的时候再上色呢? 以前,唯 ...

  3. ucml 子业务组件与行为参与者业务组件

  4. 转载 使用axis2构建webservice

    axis2是可以实现webservice的一个插件,使用这个插件可以发布webservice 1:可以使用这个插件来发布webservice,可以看网址:http://clq9761.iteye.co ...

  5. python opencv画图可视化

    画直线 import numpy as np import cv2 # Create a black image img = np.zeros((512,512,3), np.uint8) # Dra ...

  6. OnlineJudgeServer运行

    我在这如下这两篇文章都说过青岛OJ相关的 OnlineJudgeFE之前端二次开发 青岛大学开源OJ平台搭建 今天我还是要来讲讲关于这个青岛OJ. 青岛OJ采用的是完全的前后端分离架构. Online ...

  7. all与any的用法

    all函数:检测矩阵中是否全为非零元素 any函数:检测矩阵中是否有非零元素,如果有,则返回1,否则,返回0.用法和all一样 语法: B = all(A) B = all(A, dim) 复制代码 ...

  8. Zabbix 3.4.2 install && Configuration

    原理: 1)zabbix server:负责接收agent发送的报告信息的核心组件,所有配置.统计数据及操作数据都由它组织进行: 2)database storage:专用于存储所有配置信息,以及由z ...

  9. Objective-C NSTableView重点知识汇总

    NSTableView不可滚动,通常将其嵌入NSScrollView以支持NSTableView. Cell Based View Based 1.遵循协议NSTableViewDataSource, ...

  10. linux 用xshell工具远程登录

    1.设置linux,获取ip,登录名.密码 2.xshell登录