Bison眼中的iOS开发多线程是这样的(二)

allluckly.cn.png

"多线程很容易突然出现“错误情况”,这是由于系统的线程调度具有一定的随机性造成的。不过,即使程序偶然出现问题,那也是由于编程不当所引起的。当使用多个线程来访问同一个数据时,很容易“偶然”出现线程安全问题。"

前面在《Bison眼中的iOS开发多线程是这样的(一)》一文中讲到多线程的优先级,接下来我们讲讲线程同步与线程通信 多线程很容易突然出现“错误情况”,这是由于系统的线程调度具有一定的随机性造成的。不过,即使程序偶然出现问题, 那也是由于编程不当所引起的。当使用多个线程来访问同一个数据时,很容易“偶然”出现线程安全问题。

关于线程安全问题,有OC的多线程支持引入了同步,使用同步的通用方法就是@synchronized修饰代码块, 被@synchronized修饰的代码块可简称为同步代码块。同步代码块的语法格式如下:

@synchronized(obj){
    ...
    //此处的代码即为同步代码块
}

上面语法格式中@synchronized后面括号里的obj就是同步监视器。上面代码的含义是: 线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。值得注意的是,人和时刻只能有 一个线程可以获得对同步监视器的锁定,当同步代码块执行完后,该线程会释放对同步监视器的锁定 虽然OC允许使用任何对象作为同步监视器,但想一下同步监视器的目的:阻止俩个线程对同一共享资 源进行并发访问,因此通常推荐使用可能被并发访问的共享资源充当同步监视器

线程安全的类具有如下特征:

该类的对象可以被多个线程安全的访问。

每个线程调用该对象的任意方法之后都将得到正确结果。

每个线程调用该对象的任意方法之后,该对象依然保持合理状态。

Foundation框架中很多类都是有可变和不可变俩种版本,其中不可变类总是线程安全的,因为它的 对象状态不可改变。而可变类的对象需要额外的方法来保证其线程安全。 将多个线程并发修改共享资源的临界区使用@synchronized修饰,这样即可保证任意时刻,最多只 能有一个线程进入临界区修改共享数据,从而就可以实现线程安全的类。 可变类的线程安全是以降低程序的运行效率作为代价的。为了减少线程安全所带来的负面影响,程序可 以采用如下策略:

不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源(共享资源)对方法进行同步。

如果可变类有俩种运行环境:单线程环境和多线程环境,则应该为该可变类提供俩种版本,线程不安全版本和安全版本。在单线程环境中使用线程不安全版本已保证性能,在多线程环境中使用线程安全版本。

任何线程在进入同步代码块之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器的锁定呢?程序无法显示释放对同步监视器的锁定,线程会再如下几种情况下释放对同步监视器的锁定。

当线程的同步代码块执行结束,当前线程即将释放同步监视器。

当线程的同步代码块中遇到 goto、 return终止了该代码块、该方法的继续执行时,当前线程将会释放同步监视器。

当线程的同步代码块中出现了错误,导致该代码块异常结束时,将会释放同步监视器。典型的例子有:当程序调用NSThreadsleepXxx方法暂停线程时,线程不会释放同步监视器。

Foundation还提供了NSLock,通过显示定义同步锁对象来实现同步,在这种机制下,同步锁使用NSLock对象充当。

NSLock是控制多个线程对共享资源进行访问的工具。通常锁提供了对共享资源的独占访问,每次只能有一个线程对NSLock对象加锁,线程开始访问共享资源之前应县获得NSLock对象。

在实现线程安全的控制中,使用该NSLock对象可以显式的加锁、释放锁。下面我们来举个简单的?

NSLock* lock;
- (id)init
{
    self = [super init];
    if (self) {
        lock = [[NSLock alloc] init];
    }
    return self;
}

//定义需要保证线程安全的方法
- (void)Safety{
    
    [lock lock];

    //需要保证线程安全的方法
    
    [lock unlock];
}

当线程在系统内运行,线程的调度具有一定的透明度,程序通常无法准确控制线程的轮换执行,但我们可以通过一些机制来保证线程协调运行,也就是处理线程之间的通信。

对此Foundation提供了NSCondition类来处理多线程之间的通信,NSCondition实现了NSLock协议,因此也可以调用lockunlock来实现线程同步。除此之外,NSCondition可以让那些已经锁定NSCondition对象却无法继续执行的线程释放NSCondition对象,NSCondition对象也可以唤醒其他处于等待状态的线程。

NSCondition提供了如下几个方法

//改方法导致当前线程一直等待,直到其他线程调用该NSCondition的signal方法或者broadcast方法 
- (void)wait;
//用于控制等待到指定时间点,如果到了该时间点,该线程将会被自动唤醒
- (BOOL)waitUntilDate:(NSDate *)limit;
//唤醒等待的单个线程,如果有多个,则随机唤醒一个。只有当前线程放弃对该NSCondition对象的锁定后(wait方法),才可执行
- (void)signal;
//唤醒等待的所有线程
- (void)broadcast;

今天暂时写到这! 预告:(三)主讲GCD多线程!

如对你有帮助,请不要吝惜你的star和喜欢哦! 技术交流群:534926022(免费) 511040024(0.8/人付费)

推荐一款学习iOS开发的app_____|______| | 传送门

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我的博客

YII使用命令行模式

入口文件:shell.php run(); 数据库配置文件console.php可以参考main.php 演示protected/commands/TestCo...

3463
来自专栏程序员互动联盟

【专业技术第七讲】linux下如何编译C语言?

存在问题: 小伙伴们都知道gcc -c -o 但是其中的过程和一些参数就不是太清楚了,往往编译出错不知道咋办? 解决方案: 我们来简单普及一下,让大家不盲从请...

3505
来自专栏chenssy

【死磕Java并发】—–J.U.C之Condition

在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait()、notify()系列方法可以实现等待/通知模式。在Java SE...

3384
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(十二) ——Redis键的生存时间与过期时间

《Redis设计与实现》读书笔记(十二) ——Redis键的生存时间与过期时间 (原创内容,转载请注明来源,谢谢) 1、设置方式 在redis客户端,可以通过e...

2764
来自专栏海天一树

小朋友学Python(17):文件

Python 提供了必要的函数和方法进行默认情况下的文件基本操作。你可以用 file 对象做大部分的文件操作。 一、打开和关闭文件 例1 (1)创建名为test...

2745
来自专栏乐百川的学习频道

Python学习笔记 模块介绍

模块 导入模块 Python官方教程让我们在Python解释器中练习。但是当我们结束解释器,所有的代码都消失了。如果我们希望让代码永久保存的话,就需要将它们保存...

1826
来自专栏锦小年的博客

python学习笔记5.3-包的创建

包,也可以称为库,是具有很多功能的一个集合体。本文主要介绍如何自己创建一个包,以及介绍一些在包的创建过程中的技巧。 1. 包的创建 本文的例子将使用最复杂的情况...

2468
来自专栏用户2442861的专栏

linux动态库和静态库

http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520101023104745738/

2552
来自专栏大前端_Web

NodeJS学习二CommonJS规范

Node程序由许多个模块组成,每个模块就是一个文件。Node模块采用了CommonJS规范。

1122
来自专栏游戏开发那些事

【Linux程序设计】之进程控制&守护进程

这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的。

1532

扫码关注云+社区