iOS多线程系列之二: NSThread

第二部分 NSThread

先看一段API文档的描述
An NSThread object controls a thread of execution. Use this class when you want to have an Objective-C method run in its own thread of execution. Threads are especially useful when you need to perform a lengthy task, but don’t want it to block the execution of the rest of the application. In particular, you can use threads to avoid blocking the main thread of the application, which handles user interface and event-related actions. Threads can also be used to divide a large job into several smaller jobs, which can lead to performance increases on multi-core computers.

大概的意思是:一个NSThread对象管理一个线程的执行。当你想要将一个Objective-C方法运行在它自己独立的线程中,可以使用这个类。当你想执行一个比较耗时(冗长)的操作而又不想阻塞程序其他部分的运行状态时,线程是特别有用的。尤其是你可以使用线程来避免阻塞主线程处理用户界面以及和事件相关的活动。线程可以将待处理任务分割成小任务以提高多核计算机的性能。

一、NSThread的使用

1.线程的创建

方式一:

    
    / *  创建并启动线程
     *
     *  参数1要执行的方法
     *  参数2提供selector的对象,通常是self
     *  参数3传递给selector的参数
     */
    [NSThread detachNewThreadSelector:(nonnull SEL)> toTarget:(nonnull id) withObject:(nullable id)]

方式二:

//参数一:提供selector的对象,通常是self,参数2:要执行的方法,参数3:传递给selector的参数(如果selector方法不带参数,就使用nil)
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(doSomething) object:nil];

方式三:

//隐式创建并启动线程,第一个参数为调用的方法,第二个参数为传给selector方法的参数
- (void)performSelectorInBackground:(SEL)aSelector
                         withObject:(id)arg

NSThread对象的常见属性

  
    //只读属性,线程是否在执行
    thread.isExecuting;
    //只读属性,线程是否被取消
    thread.isCancelled;
    //只读属性,线程是否完成
    thread.isFinished;
    //是否是主线程
    thread.isMainThread;
    
    //线程的优先级,取值范围0.0到1.0,默认优先级0.5,1.0表示最高优     //先级,优先级高,CPU调度的频率高
    thread.threadPriority;
    
    //线程的堆栈大小,线程执行前堆栈大小为512K,线程完成后堆栈大小       为0K
    //注意:线程执行完毕后,由于内存空间被释放,不能再次启动
    thread.stackSize;

NSThread对象的方法

 //线程开始,线程加入线程池等待CPU调度(并非真正开始执行,只是通常等待时间都非常短,看不出效果)
    [thread start];
    if(!thread.isCancelled){//在执行之前需要先确认线程状态,如果已经取消就直接返回
        [thread cancel]; //通知线程取消,可以在外不终止线程执行
    }else{
        return;
    }

2.NSThread的类方法

类方法都用在线程内部,也就是说类方法作用于包含本行类方法的线程。

<1>当前线程,在开发中常用于调试,适用于所有多线程计数,返回一个线程号码

//number == 1 表示主线程,number != 1表示后台线程
int number = [NSThread currentThread];

<2>阻塞方法

//休眠到指定时间
[NSThread sleepUntilDate:[NSDate date]];
//休眠指定时长
[NSThread sleepForTimeInterval:4.5];

<3>其他类方法

//退出线程
[NSThread exit];
//当前线程是否为主线程
[NSThread isMainThread];
//是否多线程
[NSThread isMultiThreaded];
//返回主线程的对象
NSThread *mainThread = [NSThread mainThread];

3.线程的状态

线程的状态
]
<1>新建:实例化对象
<2>就绪:向线程对象发送 start 消息,线程对象被加入“可调度线程池”等待 CPU 调度;detach 方法和 performSelectorInBackground 方法会直接实例化一个线程对象并加入“可调度线程池”
<3>运行:CPU 负责调度“可调度线程池”中线程的执行,线程执行完成之前,状态可能会在“就绪”和“运行”之间来回切换,“就绪”和“运行”之间的状态变化由 CPU 负责,程序员不能干预
<4>阻塞:当满足某个预定条件时,可以使用休眠或锁阻塞线程执行,影响的方法有:sleepForTimeInterval,sleepUntilDate,@synchronized(self)x线程锁;
线程对象进入阻塞状态后,会被从“可调度线程池”中移出,CPU 不再调度
<5>死亡
死亡方式

正常死亡:线程执行完毕
非正常死亡:线程内死亡--->[NSThread exit]:强行中止后,后续代码都不会在执行
线程外死亡:[threadObj cancel]--->通知线程对象取消,在线程执行方法中需要增加 isCancelled 判断,如果 isCancelled == YES,直接返回

死亡后线程对象的 isFinished 属性为 YES;如果是发送 calcel 消息,线程对象的 isCancelled 属性为YES;死亡后 stackSize == 0,内存空间被释放。

4.多线程的安全问题

多个线程访问同一块资源进行读写,如果不加控制随意访问容易产生数据错乱从而引发数据安全问题。为了解决这一问题,就有了加锁的概念。加锁的原理就是当有一个线程正在访问资源进行写的时候,不允许其他线程再访问该资源,只有当该线程访问结束后,其他线程才能按顺序进行访问。对于读取数据,有些程序设计是允许多线程同时读的,有些不允许。

解决多线程安全问题
<1>互斥锁

// 注意:锁定1份代码只用1把锁,用多把锁是无效的
@synchronized(锁对象) { // 需要锁定的代码  }

使用互斥锁,在同一个时间,只允许一条线程执行锁中的代码.因为互斥锁的代价非常昂贵,所以锁定的代码范围应该尽可能小,只要锁住资源读写部分的代码即可。使用互斥锁也会影响并发的目的。

<2>原子属性

@property (strong, nonatomic) UIWindow *window;

atomic:能够实现“单写多读”的数据保护,同一时间只允许一个线程修改属性值,但是允许多个线程同时读取属性值,在多线程读取数据时,有可能出现“脏”数据 - 读取的数据可能会不正确。原子属性是默认属性,如果不需要考虑线程安全,要指定 nonatomic。

atomic(原子属性)在setter方法内部加了一把自旋锁
nonatomic(非原子属性)下,set和get方法都不会加锁,消耗资源小适合内存小的移动设备

UIKit中几乎所有控件都不是线程安全的,因此需要在主线程上更新UI

原子属性内部使用的 自旋锁
自旋锁和互斥锁的区别

共同点: 都可以锁定一段代码。 同一时间, 只有线程能够执行这段锁定的代码

区别:互斥锁,在锁定的时候,其他线程会睡眠,等待条件满足,再唤醒
自旋锁,在锁定的时候, 其他的线程会做死循环,一直等待这条件满足,一旦条件满足,立马去执行,少了一个唤醒过程

// 在主线程更新UI,有什么好处?

  1. 只在主线程更新UI,就不会出现多个线程同时改变 同一个UI控件
  2. 主线程的优先级最高。也就意味UI的更新优先级高。 会让用户感觉很流畅

开发建议

1.所有属性都声明为nonatomic
2.尽量避免多线程抢夺同一块资源
3.尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

5.自动释放池和运行循环

<1>运行循环
作用:保证程序不退出,坚挺所有事件,例如:手势触摸,网络加载等
特性:没有事件时,会休眠(省电),一旦监听到事件,会立即响应,每一个线程都有一个 runloop,但是只有主线程的 runloop 会默认启动。

<2>自动释放池
工作原理:自动释放池被销毁或耗尽时会向池中所有对象发送 release 消息,释放所有 autorelease 的对象!
创建和销毁:每一次运行循环启动后会创建自动释放池;程序执行过程中,所有 autorelease 对象在出了作用域之后,会被添加到最近创建的自动释放池中;运行循环结束前,会释放自动释放池。
自动释放池在ARC中同样需要。
工作原理图:

Screen Shot 2016-05-03 at 20.54.21.png

常见面试题:

int largeNumber = 2 * 1024 * 1024;
// 问题:(1)以下代码是否存在问题?(2)如果有,怎么修改?

for (int i = 0; i < largeNumber; i++) {
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"Hello "];
        str = [str uppercaseString];
        str = [str stringByAppendingString:@" - World"];
    }
}

网上的解决办法:
1)@autoreleasepool 放在外面,保证循环之后释放循环中的自动释放对象
2)@autoreleasepool 放在内部,每一次循环之后,都倾倒一次自动释放池,内存管理是最好的,但是性能不好!

6.线程通信(方法继承自NSObject)

Screen Shot 2016-05-04 at 00.09.18.png
//在主线程上执行操作,例如给UIImageVIew设置图片
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
//在指定线程上执行操作
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wai

iOS多线程系列之一:多线程基础
iOS多线程系列之二: NSThread
iOS多线程系列之三:GCD
iOS多线程系列之四:NSOperation以及多线程技术比较

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容