iOS 网络

  1. 说下线程和进程的区别?(要注意强调进程通信和同步)

一个程序运行后,就是一个进程,默认会开启一个线程,叫做主线程,这个线程主要用于显示/刷新界面和处理UI事件(比如点击事件,滚动事件,拖拽事件),要注意的是不要将比较耗时的操作放到主线程,耗时的操作会卡主主线程,影响用户体验,如果程序中有比较耗时的操作,比如网络请求,就会创建一个线程用于网络请求, 所以线程是程序的基本执行单元, 一个进程可以有多个线程,但是一个进程至少有一个线程.系统资源是分配给进程的,被进程的所有线程共享

知识点
  • 进程

    1. 进程是指系统中正在运行的一个应用程序
    2. 每个进程之间是相互独立的,每个进程均运行在其专用且受保护的内存空间内
  • 线程

    1. 线程是进程的基本执行单元, 一个进程(程序)的所有任务都在线程中执行
  • 进程与线程的区别

    1. 一个线程只能属于一个进程,而一个进程可以有多个线程, 但至少有一个线程, 线程是操作系统可识别的最小执行和调度单位
    2. 资源分配给进程, 同一进程的所有线程共享该进程的所有资源
  • 多线程(一个进程可以开启多条线程, 每条线程可以并行执行不同的任务,这个技术可以提高程序的执行效率)

  • 多线程原理

    1. 同一时间, CPU只能处理一条线程,只有一条线程在工作
    2. 多线程并发执行,其实是CPU快速在多条线程之间切换
    3. 如果CPU切换的足够快,就造成了多线程并发执行的假象
多线程优点
  • 能提高程序的执行效率
  • 能提高CPU,内存的利用率
多线程缺点
  • 创建多线程是有开销的, 开辟空间, 线程间的通信问题
  • 开启大量线程,会降低程序的性能(线程越多, 在调度线程上的开销就越大)
  • 线程之间的通信, 多线程的数据共享
多线程中会出现的问题
  • 临界代码段 - 指的是不能同时被两个线程访问的代码段, 比如一个变量,被并发进程访问后可能会改变变量值, 造成数据污染(数据共享问题)
  • 竞态条件 - 当多个线程同时访问共享数据时, 会放生争用情形,第一个线程读取改变了一个变量的值, 第二个线程也读取改变了这个变量的值, 两个线程通知操作了该变量, 此时他们会发生竞争来看哪个线程会最后写入这个变量, 最后写入的值将会被保留下来
  • 死锁 - 多个线程都要等待对方完成某个操作才能进行下一步, 这时就发生了死锁
  • 线程安全 - 一段线程安全的代码(对象), 可以同时被多个线程或并发的任务调度,不会产生问题,非线程安全的只能按次序被访问
如何保证线程安全
  1. 所有Mutable(可变)对象都是非线程安全的, 所有Immutable对象都是线程安全的, 使用Mutable对象,一定要用同步锁来同步来访问(@synchronized)
  2. 互斥锁: 能够防止多线程抢夺资源造成的数据安全问题, 但是需要消耗大量的资源
  3. 原子属性(atomic)加锁 atomic和nonatomic是否为属性的setter方法加锁
iOS中多线程的实现方案
线程生命周期
  • 新建:实例化线程对象
  • 就绪:向线程对象发送start消息, 线程对象被加入可调度线程池等待CPU调度
  • 运行:线程执行
  • 阻塞:若需要,可以使线程休眠或者锁, 阻塞线程执行
  • 死亡: 线程执行完毕或被终止
  • 还有exit和cancel
  1. pthread(基于C,跨平台,使用难度大,需要程序员管理线程生命周期)
  2. NSThread(基于OC, 简单易用,可直接操作线程对象,需要程序员管理线程生命周期)
  3. GCD(基于C,用于替代NSThread的线程技术, 自动管理线程声明周期)
  4. NSOperation(基于GCD,就是让GCD更加面向对象, 自动管理线程声明周期)
NSOperation和GCD

GCD优点: GCD主要与block结合使用,代码简洁高效

  1. NSOperation拥有更多的函数可用,NSOperationQueue是在GCD基础上实现的,是GCD更高一层的抽象
  2. 在NSOperationQueue中, 可以建立各个NSOperation之间的依赖关系
  3. NSOperationQueue支持KVO,可以监测operation是否在执行(isExecuted), 是否结束(isFinished),是否取消(isCanceld)
  4. GCD只支持FIFO的队列(FIFO:先进先出), 而NSOperationQueue可以调整队列的执行顺序(通过调整权重),NSOperationQueue可以方便的管理并发,NSOperation之间的优先级
使用NSOperationQueue的情况
  1. 各个操作之间有依赖关系
  2. 操作需要取消, 暂停
  3. 并发管理
  4. 控制操作之间的优先级
  5. 限制同时执行的线程数量
  6. 让线程在某时刻停止/继续
使用GCD的情况

一般的需求很简单的多线程操作,用GCD

GCD(Grand Central Dispatch)大中枢派发
    //回到主线程操作
    //1
    [self performSelectorOnMainThread:@selector(downLoad) withObject:nil waitUntilDone:YES];
    //2,GCD
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"要执行的操作");
    });
    //3,NSOperationQueue
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSLog(@"要执行的操作");
    }];
GCD核心概念 任务和队列

有两个执行任务的函数:(同步,异步):区别是会不会创建的新的线程 有两种队列(并行,串行)

- 串行:顺序执行
- 并行:有多少线程就在所有线程中同时执行任务
GCD常用
//GCD默认提供的全局并行队列 参数1: 队列的优先级  参数2: 保留参数
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //GCD默认提供的全局串行队列 - 主队列
    dispatch_queue_t main = dispatch_get_main_queue();
    //手动创建并发队列  参数1: 队列名 参数2: 队列属性
    dispatch_queue_t queueCreate = dispatch_queue_create("Never", DISPATCH_QUEUE_CONCURRENT);
    //GCD线程间通信
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //耗时操作
        dispatch_async(dispatch_get_main_queue(), ^{
           //回主线程
        });
    });
GCD其他用法
//GCD延迟操作
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //要执行的代码
    });
    
    //GCD一次性执行
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //只会执行一次的代码: 如创建单例
    });
    
    //GCD栅栏函数 - 在异步函数中控制任务的执行顺序, 只有当栅栏函数执行完毕之后才会执行后面的任务
    //注意: 栅栏函数不能使用全局并发队列
    dispatch_barrier_async(dispatch_get_main_queue(), ^{
        
    });
    
    //GCD的快速遍历
    dispatch_apply(1000, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSLog(@"%ld", index);
    });
    
    //GCD调度组(Dispatch_groups): Dispatch_group会在整个组的任务都完成时发出通知, 这些任务可以是同步的, 也可以是异步的, 即便在不同的队列也行, 对多个异步任务的完成进行监控
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //创建一个dispatch_group
        dispatch_group_t group = dispatch_group_create();
        //手动通知进入dispatch_group_t(必须保证 dispatch_group_enter 和 dispatch_group_leave成对出现)
        dispatch_group_enter(group);
        NSLog(@"任务一");
        [NSThread sleepForTimeInterval:3.0];
        NSLog(@"任务二");
        NSLog(@"任务三");
        //手动通知结束
        dispatch_group_leave(group);
        //等待所有任务执行完毕
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_main_queue(), ^{
            //刷新UI
        });
    });
    
    //用法二
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 创建一个dispatch_group
        dispatch_group_t group = dispatch_group_create();
        // 手动通知进入dispatch_group_t(你必须保证 dispatch_group_enter 和 dispatch_group_leave 成对出现,否则你可能会遇到诡异的崩溃问题)
        dispatch_group_enter(group);
        NSLog(@"任务1");
        [NSThread sleepForTimeInterval:3.0];
        NSLog(@"任务2");
        NSLog(@"任务3");
        // 手动通知结束dispatch_group_t
        dispatch_group_leave(group);
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"拉回主线程刷新UI");
            
        });
        
    });
        
GCD可以取消, 暂停和重新开始任务吗?

iOS8之后可以调用dispatch_block_cancel来取消

- (void)gcdBlockCancel{
     
    dispatch_queue_t queue = dispatch_queue_create("com.gcdtest.www", DISPATCH_QUEUE_CONCURRENT);
     
    dispatch_block_t block1 = dispatch_block_create(0, ^{
        sleep(5);
        NSLog(@"block1 %@",[NSThread currentThread]);
    });
     
    dispatch_block_t block2 = dispatch_block_create(0, ^{
        NSLog(@"block2 %@",[NSThread currentThread]);
    });
     
    dispatch_block_t block3 = dispatch_block_create(0, ^{
        NSLog(@"block3 %@",[NSThread currentThread]);
    });
     
    dispatch_async(queue, block1);
    dispatch_async(queue, block2);
    dispatch_block_cancel(block3);
}

但是: dispatch_block_cancel也只能取消尚未执行的任务, 对正在执行的任务不起作用

dispatch_suspend: 暂停
dispatch_resume: 恢复执行
NSOperation:配合使用NSOperation和NSOperationQueue实现多线程编程
  1. 先将需要执行的操作封装到一个NSOperation对象中
  2. 然后将NSOperation对象添加到NSOperationQueue中
  3. 系统会自动将NSOperationQueue中的NSOperation取出来
  4. 取出来的NSOperation封装的操作放到一条新线程中执行
创建操作

NSOperation是个抽象类, 不能用来封装操作, 只有使用它的子类来封装操作

  1. 使用子类NSInvocationOperation
  2. 使用子类NSBlockOperation
  3. 自定义继承自NSOperation的子类, 通过实现内部相应的方法来封装操作
   //1. 创建NSInvocationOperation对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
    //2. 调用start方法执行操作
    [op start];
    
    //1. 创建NSBlockOperation对象
    NSBlockOperation *opBlock = [NSBlockOperation blockOperationWithBlock:^{
        //耗时操作
    }];
    //2. 调用start开始执行操作
    [opBlock start];
创建队列

NSOperationQueue一共有两种队列:主队列,自定义队列. 其中自定义队列同时包含并发功能

 //主队列
    NSOperationQueue *queueMain = [NSOperationQueue mainQueue];
    //自定义队列
    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
最大并发操作数maxConcurrentOperationCount
  • maxConcurrentOperationCount 默认情况下为-1, 表示不进行限制, 可进行并发执行
  • maxConcurrentOperationCount = 1时, 表示是串行队列
  • maxConcurrentOperationCount > 1时, 并发队列
操作依赖addDependency
  • addDependency 添加依赖
  • removeDependency 移除依赖
  • dependencies 属性dependencies是一个数组, 表示当前操作开始前要完成这个数组内的所有操作
NSOperation优先级queuePriority
// 优先级的取值 
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {           NSOperationQueuePriorityVeryLow = -8L, 
NSOperationQueuePriorityLow = -4L, 
NSOperationQueuePriorityNormal = 0, 
NSOperationQueuePriorityHigh = 4, 
NSOperationQueuePriorityVeryHigh = 8 
};

NSOperation, NSOperationQueue线程同步和线程安全(NSOperation非线程安全)
  • 线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
    若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作(更改变量),一般都需要考虑线程同步,否则的话就可能影响线程安全。
  • 线程同步:可理解为线程 A 和 线程 B 一块配合,A 执行到一定程度时要依靠线程 B 的某个结果,于是停下来,示意 B 运行;B 依言执行,再将结果给 A;A 再继续操作

要安全就加锁(GCD也不是线程安全的)

NSOperation常用
  • (void)cancel; 取消操作
  • (BOOL)isFinished;是否结束
  • (BOOL)isCancelled;是否取消
  • (BOOL)isExecuting;是否正在运行
  • (BOOL)isReady;是否准备就绪
  • (void)waitUntilFinished; 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
  • (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
  • (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
  • (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
    @property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperationQueue常用属性和方法
  • 取消/暂停/恢复操作

    • (void)cancelAllOperations; 可以取消队列的所有操作。
    • (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
    • (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
  • 操作同步

    • (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。
  • 添加/获取操作`

    • (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。
    • (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
    • (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
    • (NSUInteger)operationCount; 当前队列中的操作数。
  • 获取队列

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

推荐阅读更多精彩内容