扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
线程的不安全是由于多线程访问和修改共享资源而引起的不可预测的结果。
创新互联一直通过网站建设和网站营销帮助企业获得更多客户资源。 以"深度挖掘,量身打造,注重实效"的一站式服务,以做网站、网站制作、移动互联产品、营销型网站建设服务为核心业务。十载网站制作的经验,使用新网站建设技术,全新开发出的标准网站,不但价格便宜而且实用、灵活,特别适合中小公司网站制作。网站管理系统简单易用,维护方便,您可以完全操作网站资料,是中小公司快速网站建设的选择。
ios多线程开发中为保证线程的安全常用到的几种锁: NSLock 、 dispatch_semaphore 、 NSCondition 、 NSRecursiveLock 、 @synchronized 。
WEAKSELF typeof(self) __weak weakSelf = self;
NSLock 是OC层封装底层线程操作来实现的一种锁,继承NSLocking协议。不能迭代加锁,如果发生两次lock,而未unlock过,则会产生死锁问题。
以车站购票为例,多个窗口同时售票(同步),每个窗口有人循环购票:
原子操作
原子操作是指不可打断的操作,也就是说线程在执行操作的过程中,不会被操作系统挂起,而是一定会执行完,
变量属性Property中的原子定义
一般我们定义一个变量@property (nonatomic ,strong)NSLock *lock;nonatomic:非原子性,不会为setter方法加锁,适合内存小的移动设备;atomic:原子性,默认为setter方法加锁(默认就是atomic),线程安全。
PS: 在iOS开发过程中,一般都将属性声明为nonatomic,尽量避免多线程抢夺同一资源,尽量将加锁等资源抢夺业务交给服务器。
NSCondition常用于生产者-消费者模式,它继承了NSLocking协议,同样有lock和unlock方法。条件变量有点像信号量,提供了线程阻塞和信号机制,因此可以用来阻塞某个线程,并等待数据就绪再唤醒程序。
信号量主要有3个函数,分别是:
注意: 正常的使用顺序是先降低然后提高,这两个函数通常都是成对出现。
本文主要参考了这篇文章(
),并对其中所能理解的部分进行一一验证,以前没怎么写过类似的,如果有什么做的不好的地方还请大家多多见谅!
要保证线程安全,就必须要线程同步,而在iOS中线程同步的方案有:
在 iOS 中,原子操作可以保证属性在单独的 setter 或者 getter 方法中是线程安全的,但是不能保证多个线程对同一个属性进行读写操作时,可以得到预期的值,也就是原子操作不保证线程安全,例如:
在 Objective-C 中,可以在设置属性的时候,使用 atomic 来设置原子属性,保证属性 setter 、 getter 的原子性操作,底层是在 getter 和 setter 内部使用 os_unfair_lock 加锁
在 Swift 中,原生没有提供原子操作,可以使用 DispatchQueue 的同步函数来达到同样的效果
pthread 表示 POSIX thread ,是 POSIX 标准的 unix 多线程库,定义了一组跨平台的线程相关的API。 pthread_mutex 是一种用 C 语言实现的互斥锁,有单一的拥有者
递归锁是一种特殊互斥锁。递归锁允许单个线程在释放之前多次获取锁,其他线程保持睡眠状态,直到锁的所有者释放锁的次数与获取它的次数相同。递归锁主要在递归迭代中使用,但也可能在多个方法需要单独获取锁的情况下使用。
pthread_mutex 支持递归锁,只要把 attr 的类型改成 PTHREAD_MUTEX_RECURSIVE 即可,它有单一的拥有者
NSRecursiveLock 是以 Objective-C 对象的形式对 pthread_mutex(Recursive) 的封装,它有单一的拥有者
条件锁是一种特殊互斥锁,需要条件变量(condition variable) 来配合。条件变量有点像信号量,提供了线程阻塞与信号机制,因此可以用来阻塞某个线程,并等待某个数据就绪,随后唤醒线程。条件锁是为了解决 生产者-消费者模型
pthread_mutex 配合 pthread_cond_t ,可以实现条件锁,其中 pthread_cond_t 没有拥有者
NSCondition 是以 Objective-C 对象的形式对 pthread_mutex 和 pthread_cond_t 进行了封装, NSCondition 没有拥有者
NSConditionLock 是对 NSCondition 的进一步封装,可以设置条件变量的值。通过改变条件变量的值,可以使任务之间产生依赖关系,达到使任务按照一定的顺序执行,它有单一的拥有者(不确定)
读写锁是一种特殊互斥锁,提供"多读单写"的功能,多个线程可以同时对共享资源进行读取,但是同一时间只能有一条线程对共享资源进行写入
pthread_rwlock 有多个拥有者
性能从高到底分别是:
总结:
苹果官方文档
白夜追凶,揭开iOS锁的秘密
起底多线程同步锁(iOS)
深入理解 iOS 开发中的锁
1.进程
具有一定独立功能的程序关于某次数据集合的一次运行活动,他是操作系统分配资源的基本单位。
进程是指系统正在运行的一个应用程序,就是一段程序的执行过程。
各进程之间相互独立,每个进程都运行在受保护的内存空间内,拥有独立运行所需的全部资源。
2. 线程
进程的最小执行单元。线程是一个进程的实体。
一个进程要想执行任务,就必须至少又一个线程,当程序启动时,系统默认开启一条线程,也就是主线程。
3、 进程与线程的关系
线程是进程的执行单元,进程的所有任务都在线程中执行
线程是CPU分配和执行任务的最小单元
一个程序可以有多进程,一个进程可以有多线程 ,但是一个进程至少有一个线程
同一个进程内的线程共享进程内的资源
4 多线程原理
同一时间内单核CPU 只执行一个线程,多线程是CPU 在单位时间内在多个线程之间切换,造成多个线程被同时执行的假象。
多核CPU 就可以执行多个线程
多线程的目的是同步执行多个线程,来提高运行效率
5、多线程的优缺点
有点:提高执行效率 能够提高CPU的使用率
缺点:占用一定的系统内存空间 线程越多 CPU 调度线程的开销越大 程序设计复杂(线程之间的通信 数据共享)
6、多线程并发 与并行区别
并发:在一条线程上快速切换
并行: 利用多核CPU ,多线程同时进行
7、IOS 中的多线程
NSThread 需要手动创建 不需要销毁 子线程通信很难
GCD c语言,充分利用了设备的多核,自动管理线程生命周期。比NSOperation效率更高。
NSOperation 基于gcd封装,更加面向对象,比gcd多了一些功能。
8、多个网络请求如何执行下一步
使用GCD的dispatch_group_t
创建一个dispatch_group_t
每次网络请求前先dispatch_group_enter,请求回调后再dispatch_group_leave,enter和leave必须配合使用,有几次enter就要有几次leave,否则group会一直存在。
当所有enter的block都leave后,会执行dispatch_group_notify的block。
NSString *str = @"";
NSURL*url = [NSURLURLWithString:str];
NSURLRequest*request = [NSURLRequestrequestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_tdownloadGroup =dispatch_group_create();
for ( int i=0; i10; i++) {
dispatch_group_enter(downloadGroup);
NSURLSessionDataTask*task = [sessiondataTaskWithRequest:requestcompletionHandler:^(NSData* _Nullable data,NSURLResponse* _Nullable response,NSError* _Nullable error) {
NSLog(@"%d---%d",i,i);
dispatch_group_leave(downloadGroup);
}];
[taskresume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
NSLog(@"end");
});
7.多个网络请求顺序执行后如何执行下一步?
使用信号量semaphore
每一次遍历,都让其dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER),这个时候线程会等待,阻塞当前线程,直到dispatch_semaphore_signal(sem)调用之后
NSString*str=@"";
NSURL*url=[NSURL URLWithString:str];
NSURLRequest*request=[NSURLRequest requestWithURL:url];
NSURLSession*session=[NSURLSession sharedSession];
dispatch_semaphore_t sem=dispatch_semaphore_create(0);
for(inti=0;i10;i++){
NSURLSessionDataTask*task=[session dataTaskWithRequest:request completionHandler:^(NSData*_Nullable data,NSURLResponse*_Nullable response,NSError*_Nullable error){
NSLog(@"%d---%d",i,i);
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_wait(sem,DISPATCH_TIME_FOREVER);
}
dispatch_async(dispatch_get_main_queue(),^{
NSLog(@"end");
});
线程和进程在我们开发中,跟我们一直形影不离,那么什么是进程,什么是线程,它们又有什么关系,这篇文章将为您简单介绍。
线程概念
进程概念
地址空间:同⼀进程的线程共享本进程的地址空间( TLS是本地的线程栈存空间,线程的局部空间是某些操作系统为线程提供的私有空间,只具备有限的容量,并不属于线程,由操作系统单独安排的 ),⽽进程之间则是独⽴的地址空间。
资源拥有:同⼀进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独⽴的。
优点:
缺点:
时间⽚的概念:CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚。
多线程同时执行
如果线程非常多
互斥锁⼩结
互斥锁参数
nonatomic⾮原⼦属性
atomic原⼦属性(线程安全),针对多线程设计的,默认值,保证同⼀时间只有⼀个线程能够写⼊(但是同⼀个时间多个线程都可以取值)
atomic本身就有⼀把锁(⾃旋锁)
单写多读:单个线程写⼊,多个线程可以读取
atomic:线程安全,需要消耗⼤量的资源
nonatomic:⾮线程安全,适合内存⼩的移动设备
iOS开发建议
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁,资源抢夺的业务逻辑交给服务器处理,减少APP的压力
这篇文章简单介绍了线程与进程的概念,烦请大家不吝赐教。
在多线程开发中,我们常用到GCD,这里探讨一下GCD任务的取消:
1.在iOS 8以后,系统给我们提供了这样的取消函数 dispatch_block_cancel,不过这个也只能用于dispatch_block_create创建的dispatch_block_t,我们试验一下:
这时肯定是任务都会执行的
接下来,把注释的那一行 dispatch_block_cancel(block1);打开,看看效果:
我们发现block1确实被取消掉了。这是dispatch_block_cancel的用法。
2.很多时候,我们的场景不会去用dispatch_block_create创建dispatch_block_t,这个时候我们若想取消一个任务,可以考虑用一个条件来做,满足条件则执行此任务,不满足则不执行,举个例子:
效果如下:
写到这里,这儿其实还隐藏了一个知识点,就是block的变量捕获,有兴趣或是不理解的朋友可以研究一下。(如下,为何输出不是20而是10)
3.过渡到NSOperation
NSOperation是对GCD的封装,底层也是GCD。
NSOperation给我们封装了更多的api,这是我在Xcode中提出来的:
我们可以发现它有状态属性,有取消方法,也有添加依赖方法等...这里我们还是先说取消吧,下面来给大家写个demo:
这时输出是:
因为正在执行的任务,NSOperation也是不能取消的,所以也是需要将cancel在start前调用的(就如同满足一个条件是否需要cancel一样,也可以满足条件不调用start)
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流