iOS多线程篇-多线程基础

导语
所有语言开发的程序,最终都是转换成汇编语言进而解释成机器编码来执行.
但机器编码是按照顺序执行的,一个复杂的多步操作只能一步步按顺序逐步执行,为了减少用户等待的时间,让程序尽可能快的完成运算,就有了多线程.

  • 什么是进程
    进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,它们均运行在其专用且受保护的内存空间内.若你此时打开了微信,又打开了QQ音乐,则系统会分别启动两个进程.

  • 什么是线程
    线程是进程的基本执行单元,1个进程若想执行任务,至少要有一个线程,例如你用QQ音乐的一个线程下载一首歌.

看下面这张图,我的Mac系统此时开了这么多进程,每个进程的线程数不一样,你可以自己写一个应用程序,也开18个线程,就会显示在这里.

13C5748E-A3AF-49AF-BAD0-CE5A52C584A8.png
  • 线程的串行
    一个线程中执行的任务是串行的,就是按照顺序一个一个来,在同一时间内1个线程只能执行一个任务.
    假如要在一个线程中下载ABC三首歌,则是A - B - C.

  • 多线程
    一个进程中可以开启多条线程,线程们可以并行执行不同的任务.
    **原理 : **同一时间内,CPU其实只能处理一条线程.
    1- 对于单核处理器,关于多线程并发执行,其实是CPU快速的在多条线程之间切换调度,如果CPU调度线程的时间足够快,就会造成多线程并发执行的假象.
    2- 对于多核处理器,关于多线程的并发执行,是多个CPU在快速的处理多个线程.

F2995926-CED7-4D31-8D6E-7998FCEF4722.png

优点 :
可同时完成多个任务,防止卡顿,适当的提高了程序的执行效率(例如开启三个线程同时下载ABC)
可适当提高资源利用率(CPU,内存利用率)
缺点 :
开启线程需要占用一定的内存空间(默认主线程占1M,子线程占512KB),若开启的多,会占用大量的内存空间,降低程序的性能.
线程越多,CPU在调度线程上的开销就越大.
多线程会使程序设计更加复杂,例如线程之间的通信,多线程数据的共享.

  • 主线程
    一个iOS程序运行后,默认会开启一条线程,即主线程,也被称为UI线程.它是其他线程的父线程,
    因为iOS中除了主线程,其他子线程都是独立于Cocoa Touch的,所以只有主线程可以更新UI界面.
    作用 :显示并刷新UI界面/处理UI事件(点击,滚动,拖拽等)
    注意 :请不要将比较耗时的操作放在主线程,因为主线程同一时间只能处理一个事件,如果它去处理耗时操作(比如下载),它就不会再去处理其他事件(比如点击按钮),造成线程阻塞,严重影响UI流畅度,给用户"卡顿"的感觉.
    所以,请将耗时操作放在后台线程或其他线程.
    举个例子:
界面放两个按钮.png
- (IBAction)onClickFor:(id)sender
{
    //获取当前线程
    NSThread *current=[NSThread currentThread];
    //使用for循环执行一些耗时操作
    for (int i=0; i<10000; i++)
    {
        //输出线程
        NSLog(@"循环---%d---%@",i,current);
    }
}
- (IBAction)onClickResponder:(id)sender
{
     NSLog(@"用户相应");
}

我首先点击了for循环的按钮,在它循环10000次的过程中,我点击了10下另一个按钮,当然没有反应,但当for循环运行完毕,就相应了我刚刚的10下点击,所以证明,主线程是串行处理事件滴

主线程串行处理事件.png
  • 多线程开发方法
    随着iOS的发展,有以下四种方式
方案 简介 语言
pthreads 需要自己管理线程生命周期 C
NSThread 需要自己管理线程生命周期 OC
GCD 自动管理线程生命周期 C
NSOperation& NSOperationQueue 自动管理线程生命周期 OC

下面分类阐述

pthreads已经几乎不用了,所以重点介绍一下后三种.

NSThread
优点:使用更加面向对象,简单易用,可直接操作线程对象
缺点:需要自己管理线程生命周期,使用互斥锁时会有一定的系统开销.

一个Thread对象就代表一条线程,下面介绍常用方法
创建并启动线程

/** 有两种方法,类方法和对象方法*/
//类方法,创建线程后自动启动
[NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
//对象方法,需要先创建一个线程对象,然后调用start方法启动线程,这样创建的好处是,可以在start前对线程进行设置,例如设置优先级
NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
[thread start];

主线程相关

//获取主线程
+ (NSThread *)mainThread;
//判断是否为主线程(类方法)
+ (BOOL)isMainThread
//判断是否为主线程(对象属性)
@property (readonly) BOOL isMainThread;

线程设置

//获取当前线程
+ (NSThread *)currentThread;
//给线程设置名字
@property (nullable, copy) NSString *name;

打印线程会看到编号和名字,需要注意:主线程的编号永远为1

未设置名字为空.png
设置后为你的名字.png

线程调度的优先级,调度优先级的取值范围是0.0
~ 1.0,默认0.5,值越大,优先级越高

+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
@property double threadPriority;

线程的状态(开始,取消,阻塞,死亡)

//线程开始
- (void)start;
//线程取消,只是更改了线程的状态,线程依然可以运行
- (void)cancel;
//线程阻塞,类方法,所有的线程都会阻塞
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//线程死亡,类方法,全死,一旦死亡,将无法再开启
+ (void)exit;

做个试验:

3FD72911-E20E-424A-BECE-2AEFF384A143.png

1- 点击按钮A,在主线程输出一句话
2- 点击按钮B,新开辟一个线程去下载网络图片,下载好的图片加载到UIImageView上
3- 点击"阻塞"按钮,让线程阻塞10S,10S期间,你会发下自己的操作(比如点击按钮A或B),都会排到等待序列中,等10S后,才会执行
4- 点击"强制停止",执行的是 [NSThread exit],则再点击任何按钮,都不再相应

线程之间的通信
1- 一个线程传递数据给另一个线程
2- 在一个线程中执行完特定任务后,转到另一个线程继续执行任务

//和主线程之间的通信,传代码块带主线程执行,更新UI必须到主线程,我测试的是无法去其他线程的
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//和指定线程之间的通信,传代码块到指定线程执行
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//直接开辟一个子线程,传代码块去他那里执行
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;

示例:

- (void)other
{
    [self performSelectorInBackground:@selector(backWork) withObject:nil];
}
-(void)backWork
{
    NSLog(@"the thread is %@",[NSThread currentThread]);
    sleep(2);
    [self performSelectorOnMainThread:@selector(mainWork) withObject:nil waitUntilDone:NO];
}

-(void)mainWork
{
    NSLog(@"the main thread is %@",[NSThread currentThread]);
}
5E5AE7FA-9C3A-4F5F-9918-5F7EEE74050A.png

线程的安全隐患
资源共享 : 多个线程可能访问同一块资源(对象,变量或文件),这样会很容易引起数据错乱或数据安全问题.
**解决方式 : **
线程同步 : 多条线程在同一条线上,按顺序执行任务
互斥锁 : 使用了线程同步技术
互斥锁使用方法 : @synchronized(锁对象) {需要锁定的代码}
注意 : 锁定一份代码只用一把锁,多把无效
互斥锁优点 : 能有效防止因多线程抢夺资源造成的数据安全问题
互斥锁缺点 : 需要消耗大量的CPU资源

做个测试

#pragma mark - 线程安全隐患
- (void)safe
{
    self.leftTicketsCount=10;
    self.thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
    self.thread1.name = @"售票员A";
    self.thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
    self.thread2.name = @"售票员B";
    self.thread3 = [[NSThread alloc]initWithTarget:self selector:@selector(sellTickets) object:nil];
    self.thread3.name=@"售票员C";
}
 -(void)sellTickets
 {
    while (1)
    {
        int count=self.leftTicketsCount;
        if (count>0)
        {
            [NSThread sleepForTimeInterval:0.2];
            self.leftTicketsCount= count-1;
            NSThread *current=[NSThread currentThread];
            NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
        }
        else
        {
            [NSThread exit];
        }
    }
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.thread1 start];
    [self.thread2 start];
    [self.thread3 start];
     
}

若加上线程锁

        @synchronized(self)//线程锁
        {
            int count=self.leftTicketsCount;
            if (count>0)
            {
                [NSThread sleepForTimeInterval:0.2];
                self.leftTicketsCount= count-1;
                NSThread *current=[NSThread currentThread];
                NSLog(@"%@--卖了一张票,还剩余%d张票",current,self.leftTicketsCount);
            }
            else
            {
                [NSThread exit];
            }
        }
8EBEDB12-266C-4660-B6FE-0CFE08E98703.png

延伸一

还记得OC定义属性时的nonatomic和atomic吗?

atomic :默认属性,原子属性,为setter方法加锁,线程安全,但需要消耗大量的资源
nonatomic : 非原子属性,不会为setter方法加锁,非线程安全,适合内存小的移动设备
建议 :
1- 建议所有属性都声明为nonatomic
2- 尽量避免多线程抢夺同一资源
3- 尽量将加锁,资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

延伸二

关于手机内存和硬盘

请看下面的图,关于本机里的总容量和可用容量是什么?
答案:是硬盘硬盘硬盘,不是内存!


1- 看我这张手绘图,若你是64G手机,安了一堆APP,都是放在你的硬盘里的,每一个APP都有一块自己的空间.
2- 当你打开一个APP,就会在上面的ROM那条,开辟一块内存空间给你用,假如你现在打开了微信,打开了QQ,打开了一堆APP后,然后打开了一个你自己写的/假如没做好内存管理的APP.
3- 假如你这个没做好内存管理的APP是,你加载一个界面的时候,循环生成了100000..个你自定义的控件,而你又没有在didReceiveMemoryWarning中做任何处理,那么运行时会出现什么情况呢?
4- 你现在开始运行你的程序,要加载你那个巨傻的界面了,内存不足,iPhone就会在didReceiveMemoryWarning中提醒你,但你没处理,iPhone就会赶紧关掉你之前打开的,在后台运行的APP,给你腾内存空间,把全部内存空间都让给你用了,还不够!!!然后就崩掉了.
5- 我们平时说的沙盒,是在硬盘里的,我们平时说的内存管理,是运行时管理上面的ROM,我们平时说的缓存,是缓存到硬盘中,运行时先从硬盘去取,硬盘没有再去下载
6- 以上是个人理解,若有错误请大家指正,感激!

PS:
硬盘与内存:
1.你手机中下载的 APP 都在硬盘中存放,当你打开了某一 APP, 它就被移到了内存中,若你按 HOME 键,APP 仍在内存中,再打开时能迅速打开,但若你将它杀掉, 则回到了硬盘,再次打开时,会缓慢的加载,因为要从硬盘移动到内存

CPU
1.它负责计算,比如我们写好一个程序,运行时会有一段加载蓝色进度条的过程,其实是 CPU 将我们写的 OC 语言转化成了计算机能读懂的二进制语言

参考网址
http://www.cnblogs.com/wendingding/p/3805088.html
http://www.cnblogs.com/kenshincui/p/3983982.html#NSOperation

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

推荐阅读更多精彩内容