iOS 多线程(I)

1. Objective-C 中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?

    线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码。

    方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:。



2. 有哪些对象和变量需要释放掉?

如:线程的queue, 绘制图片的上下文等;



3. RunLoop 的5种运行方式是什么?

kCFRunLoopDefaultMode:  
UITrackingRunLoopMode:  
UIInitializationaRunLoopMode:  
GSEventReceiveRunLoopMode:  
KCFCommonRunLoopModes:  



4.我们说的OC是动态运行时语言是什么意思?

主要是将数据类型的确定由编译时,推迟到了运行时。

简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。



5. GCD 与 NSOperation 的区别:

GCD 和 NSOperation 都是用于实现多线程:
    GCD 基于C语言的底层API,GCD主要与block结合使用,代码简洁高效。
    NSOperation 属于Objective-C类,是基于GCD更高一层的封装。复杂任务一般用NSOperation实现。



6. 写出使用GCD方式从子线程回到主线程的方法代码

dispatch_sync(dispatch_get_main_queue(), ^{ });



7. 如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

// 使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ }); 
// 当并发队列组中的任务执行完毕后才会执行这里的代码
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并图片
});



8. dispatch_barrier_async(栅栏函数)的作用是什么?

函数定义:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
作用:
    1.在它前面的任务执行结束后它才执行,它后面的任务要等它执行完成后才会开始执行。
    2.避免数据竞争

// 1.创建并发队列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向队列中添加任务
dispatch_async(queue, ^{  // 1.2是并行的
    NSLog(@"任务1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任务2, %@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"任务 barrier, %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{   // 这两个是同时执行的
    NSLog(@"任务3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任务4, %@",[NSThread currentThread]);
});

// 输出结果: 任务1 任务2 ——》 任务 barrier ——》任务3 任务4 
// 其中的任务1与任务2,任务3与任务4 由于是并行处理先后顺序不定。

参考资料:GCD(III)



9. 以下代码运行结果如何?

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
}
// 只输出:1。(主线程死锁)



10. 什么是 RunLoop?

从字面上讲就是运行循环,它内部就是do-while循环,在这个循环内部不断地处理各种任务。
一个线程对应一个RunLoop,基本作用就是保持程序的持续运行,处理app中的各种事件。通过runloop,有事运行,没事就休息,可以节省cpu资源,提高程序性能。

主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个如下的main()函数
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

参考资料:RunLoop 详细介绍



11. OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?

// 创建线程的方法
- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
- [self performSelectorInBackground:nil withObject:nil];
- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
- dispatch_async(dispatch_get_global_queue(0, 0), ^{});
- [[NSOperationQueue new] addOperation:nil];

// 主线程中执行代码的方法
- [self performSelectorOnMainThread:(SEL)aSelector  withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];



12. 你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和G.C.D的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)

使用NSOperationQueue用来管理子类化的NSOperation对象,控制
其线程并发数目。GCD和NSOperation都可以实现对线程的管理,区别
是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中
使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中
使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,
是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接
口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线
程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议
在简单项目中使用。



13. iOS 的几种线程锁?
互斥锁:用于多线程编程,防止两条线程同时对同一公共资源进行读写的机制;

@synchronized  简单、效率最低。

NSLock  不能多次调用 lock方法,会造成死锁;  

pthread_mutex



自旋锁:采用信号的一种同步方式

dispatch_semaphore  使用信号量来获取更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥;

OSSpinLock



递归锁:同一个线程可以加锁N次而不会引发死锁

NSRecursiveLock



条件锁: 条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行

NSCondition

NSConditionLock

遵循NSLocking协议,使用的时候同样是lock,unlock加解锁,wait是傻等,waitUntilDate:方法是等一会,都会阻塞掉线程,signal是唤起一个在等待的线程,broadcast是广播全部唤起。



读写锁

//加读锁
pthread_rwlock_rdlock(&rwlock);
//解锁
pthread_rwlock_unlock(&rwlock);
//加写锁
pthread_rwlock_wrlock(&rwlock);
//解锁
pthread_rwlock_unlock(&rwlock);



14. NSThread、NSOperationQueue、GCD 3种线程的认知

1)  NSThread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,
你需要自己管理thread的生命周期,线程之间的同步。线程共享同一应用程序的部分内存空间,
它们拥有对数据相同的访问权限。你得协调多个线程对同一数据的访问,
一般做法是在访问之前加锁,这会导致一定的性能开销。

2)  NSOperationQueue 以面向对象的方式封装了用户需要执行的操作,
我们只要聚焦于我们需要做的事情,而不必太操心线程的管理,同步等事情,
因为NSOperation已经为我们封装了这些事情。
NSOperation 是一个抽象基类,我们必须使用它的子类。

3)  GCD: iOS4 才开始支持,它提供了一些新的特性,以及运行库来支持多核并行编程,
它的关注点更高:如何在多个cpu上提升效率。

总结:
- NSThread是早期的多线程解决方案,实际上是把C语言的PThread线程管理代码封装成OC代码。
- GCD是取代NSThread的多线程技术,C语法+block。功能强大。
- NSOperationQueue是把GCD封装为OC语法,额外比GCD增加了几项新功能。
    * 最大线程并发数
    * 取消队列中的任务
    * 暂停队列中的任务
    * 可以调整队列中的任务执行顺序,通过优先级
    * 线程依赖
    * NSOperationQueue支持KVO。这就意味着你可以观察任务的状态属性。
但是NSOperationQueue的执行效率没有GCD高,所以一半情况下,我们使用GCD来完成多线程操作。



15. 多个网络请求完成后执行下一步,有几种解决方案?
使用dispatch_group

-(void)Btn2{
    NSString *str = @"http://www.jianshu.com/p/6930f335adba";
    NSURL *url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];

    dispatch_group_t downloadGroup = dispatch_group_create();
    for (int i=0; i<10; i++) {
        dispatch_group_enter(downloadGroup);

        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

            NSLog(@"%d---%d",i,i);
            dispatch_group_leave(downloadGroup);

        }];

        [task resume];
    }

    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
        NSLog(@"end");
    });
}

  创建一个dispatch_group_t, 每次网络请求前先dispatch_group_enter,请求回调后再dispatch_group_leave,对于enter和leave必须配合使用,有几次enter就要有几次leave,否则group会一直存在。当所有enter的block都leave后,会执行dispatch_group_notify的block。



采用信号量dispatch_semaphore_t

-(void)Btn3{
    NSString *str = @"http://www.jianshu.com/p/6930f335adba";
    NSURL *url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];

    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    for (int i=0; i<10; i++) {

        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

            NSLog(@"%d---%d",i,i);
            count++;
            if (count==10) {
                dispatch_semaphore_signal(sem);
                count = 0;
            }

        }];

        [task resume];
    }
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"end");
    });
}

dispatch_semaphore信号量为基于计数器的一种多线程同步机制。如果semaphore计数大于等于1,计数-1,返回,程序继续运行。如果计数为0,则等待。dispatch_semaphore_signal(semaphore)为计数+1操作,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)为设置等待时间,这里设置的等待时间是一直等待。
对于以上代码通俗一点就是,开始为0,等待,等10个网络请求都完成了,dispatch_semaphore_signal(semaphore)为计数+1,然后计数-1返回,程序继续执行。(这里也就是为什么有个count变量的原因,记录网络回调的次数,回调10次之后再发信号量,使后面程序继续运行)。



使用dispatch_barrier_async(栅栏函数)

dispatch_barrier_sync(dispatch_queue_t   queue, ^{

    })
  • 在它前面的任务执行结束后它才执行,它后面的任务要等它执行完成后才会开始执行,

  • 避免数据竞争
    GCD(III)



15. OC 的锁有哪些?

@synchronized.                             加锁的对象需要是同一个对象
NSLock 对象锁。                           多次lock死锁
NSRecursiveLock   递归锁。        场景限制
NSConditionLock   条件锁。
pthread_mutex (C语言)互斥锁   linux 底层
dispatch_semaphore (GCD)。  信号量
OSSpinLock (不建议使用)



16. 自旋和互斥对比?

相同点:都能保证同一时间只有一个线程访问共享资源。都能保证线程安全。

不同点: 

互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。

自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。

自旋锁的效率高于互斥锁。



17. 使用以上锁需要注意哪些?

使用自旋锁时要注意:
  由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在哪里自旋,这就会浪费CPU时间。

  持有自旋锁的线程在sleep之前应该释放自旋锁以便其它线程可以获得该自旋锁。内核编程中,如果持有自旋锁的代码sleep了就可能导致整个系统挂起。

  使用任何锁都需要消耗系统资源(内存资源和CPU时间),这种资源消耗可以分为两类:
        1.建立锁所需要的资源

        2.当线程被阻塞时所需要的资源

使用互斥锁的注意:
  由于是互斥锁,当一个线程进行访问的时候,该线程获得锁,其他线程进行访问的时候,将被操作系统挂起,直到该线程释放锁,其他线程才能对其进行访问,从而却确保了线程安全。但是如果连续锁定两次,则会造成死锁问题。

 

两种锁的加锁原理:

互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换(主动出让时间片,线程休眠,等待下一次唤醒),cpu的抢占,信号的发送等开销。

自旋锁:线程一直是running(加锁——>解锁),死循环(忙等 do-while)检测锁的标志位,机制不复杂。



``




``




``




``




``




``


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

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,372评论 8 265
  • Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么? 1...
    AlanGe阅读 1,736评论 0 17
  • 原文地址 http://www.cnblogs.com/kenshincui/p/3983982.html 大家都...
    怎样m阅读 1,268评论 0 1
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,097评论 1 32
  • 1.进程 系统中正在运行的应用程序每个进程之间是相互独立的,运行在各自独有且受保护的内存中 2.线程 1.进程是不...
    GSChan阅读 376评论 0 2