iOS开发攻城狮的集散地ios

iOS多线程(面试题)汇总分享

2018-06-25  本文已影响273人  程序犭袁

目录

为了让大家更好的交流学习,小编建立了一个交流学习的群。230811170


实际问题

这个不太可能让人真的手写代码或者上机、根据回答的点大概可以揣测一些水平吧?

说队列组/依赖基本可以确定了解GCD/NSOpertion。但是比较麻烦、用线程栅栏dispatch_barrier的话会更简便一些。

如果只说队列/任务组肯定不行。因为网络请求本身是异步的、任务会立即完成、但数据还没有回来。

最好的就是在队列组的前提下。把异步的网络请求转化为同步、以捕获正确的完成时机。

具体操作需要使用信号量。

1、每次输入字符的时候。如何进行数据遍历(如果用的For、那么为什么不用GCD的快速迭代?----因为开辟线程以及线程同步需要些许耗时、对于非耗时操作、for的性能会更好一些)。
2、每次输入字符的时候。如何废弃之前的搜索任务、以免重复插入(NSOperationQueue之类)。
3、高频次使用searchArray数组时的安全性。(锁)

问题汇总

队列为先决条件、然后再去满足任务的需求。

举个几个例子:

1、异步+串行(如果不是主队列):
可以开辟线程 + 阻塞线程 = 会开辟一个新的线程去执行任务。
2、同步+并行:
不会开辟线程 + 不能阻塞线程 = 只能将任务放置与当前线程的最后、以解放线程。
2、异步+并行:
可以开辟线程 + 不能阻塞线程 = 开辟多个线程执行任务

GCD基于C、NSOperation基于GCD的封装。

- (void)thread_bingfa { for (int a = 0; a < 1000; a ++) 
{        [self performSelectorInBackground:@selector(aaa:) withObject:[NSNumber numberWithInt:a]];    }}
- (void)aaa:(NSNumber *)number { for (int a = 0; a < 100; a++) {        sleep(1); NSLog(@"%@",number);    }}
最大并发_NSThread

最大并发_NSThread

self.operationQueue=[[NSOperationQueue alloc]init];
- (void)viewDidLoad {    for (int a = 0; a < 10000; a ++)
 {        [self bbb:a];    }}- (void)bbb:(int)a {        [self.operationQueue addOperationWithBlock:^
{            for (int i = 0; i < 100; i ++) {                sleep(10);                NSLog(@"%d",a);      
      }      
 }];}

最大并发_NSOperation

不能、只能把对应线程进行cancel标记。详见下文NSThread相关的几个坑。

分散在NSThread.h、NSRunLoop.h、NSObject.h。
详见下文一些NSObject的相关扩展方法(performSelector).

这个问题其实和解决- (id)performSelector:(SEL)aSelector withObject:(id)object下方法受限的方式一样。

1、使用字典。
2、NSInvocationOperation实际上就是方法签名NSInvocation。所以如果使用NSInvocation进行初始化也能解决参数受限的问题、只是太麻烦了、除非特定情况(目前我接触到的只有模块化的Route层)不然不推荐。

串行并行实际上是GCD的名词。
并行意味着多线程执行任务、串行意味着单线程执行任务。
任务在每一个线程内部、其实都是串行的。
NSOperation并没有串行并行的概念、自然也谈不上设置。
但是我们可以通过通过设置某个队列(NSOperationQueue)的大并发数为1、让其中任务们(NSOperation)自动被分配到不同线程中自动执行、以达到串行/并行的底层结果。

不能、优先级的判定是建立在依赖操作完成后对下一步操作的排序下。
具体可见下文NSOperation --> 操作的优先级

不是、默认的操作会被置于队列开辟的首个线程(主队列则为主线程)、剩余的操作会开辟新的线程并发执行。但是有并发数限制、由系统分配。
至于为什么这么设计。
NSBlockOperation下所有的操作默认情况下也是并行的。由并行通过依赖控制成串行容易、但由由串行想做出并行的效果则很难。
比如需要同时下载三张图片下载完成之后、将其展示。
我可以将三个下载操作追加进一个blockOperation1、再让展示操作的BlockOperation2依赖BlockOperation1。

NSOperationQueue *operationQueue=[NSOperationQueue mainQueue];   
 NSBlockOperation *blockOperation1=[NSBlockOperation blockOperationWithBlock:
^{        sleep(1);        NSLog(@"下载任务--%d",1);    }];    for (int i=2; i<4; ++i) {     
   [blockOperation1 addExecutionBlock:
^{            sleep(1);            NSLog(@"下载任务--%d",i);        }];    }   
 NSBlockOperation *blockOperation2=
[NSBlockOperation blockOperationWithBlock:
^{        sleep(1);        NSLog(@"展示任务");    }]; 
   [blockOperation2 addDependency:blockOperation1];    [operationQueue addOperation:blockOperation1]; 
   [operationQueue addOperation:blockOperation2];

打印结果

2018-03-16 16:34:02.388806+0800 test[5620:522718] 
下载任务--22018-03-16 16:34:02.388806+0800 test[5620:522602] 
下载任务--12018-03-16 16:34:02.388806+0800 test[5620:522717] 
下载任务--32018-03-16 16:34:03.390790+0800 test[5620:522602]
 展示任务

如果addExecutionBlock的操作是串行的。那么我只能创建三个下载操作、然后将展示操作依赖于以上三个操作。得不偿失。

GCD


不同线程对比

主要说GCD和NSOperation、如果NSThread方便实现的话可能会提一句。

线程切换
- (void)viewDidLoad {    [super viewDidLoad];   
 // Do any additional setup after loading the view, typically from a nib.   
 [self performSelectorInBackground:@selector(fun1) withObject:nil];}-
 (void)fun1 {    //回到主线程    [self performSelectorOnMainThread:@selector(fun2) withObject:nil waitUntilDone:nil];}
dispatch_queue_t queue = dispatch_get_global_queue
(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, ^{    // 执行耗时的异步操作...    dispatch_async(dispatch_get_main_queue(),
 ^{      // 回到主线程,执行UI刷新操作    });});
NSOperationQueue * queue 
= [[NSOperationQueue alloc]init];
//切换到子线程[queue addOperationWithBlock:
^{    [[NSOperationQueue mainQueue] addOperationWithBlock:^{        //切换回主线程    }];}];
- (void)dispatch_group_test
 {    dispatch_queue_t queue = dispatch_queue_create("queue_test", DISPATCH_QUEUE_CONCURRENT);   
 dispatch_group_t group = dispatch_group_create();    dispatch_group_async(group, queue, ^{        NSLog
(@"任务1——准备休眠3秒");       
 sleep(3);       
 NSLog(@"任务1——完成");    }); 
   NSLog(@"主线程——准备休眠5秒"); 
   sleep(5);    NSLog(@"主线休眠结束");    dispatch_group_async(group, queue, ^{        NSLog(@"任务2——准备休眠10秒");      
  sleep(10);       
 NSLog(@"任务2——完成");    });   
 dispatch_group_notify(group, queue, ^{        NSLog(@"任务组完成");    });  
  NSLog(@"主线程结束");}
//创建操作队列NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
//创建最后一个操作NSBlockOperation *lastBlockOperation=[NSBlockOperation blockOperationWithBlock:^{    sleep(1);   
 NSLog(@"最后的任务");}];for (int i=0; i<5-1; ++i)
 {    //创建多线程操作    NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:
{        sleep(i);        NSLog(@"第%d个任务",i);    }];   
 //设置依赖操作为最后一个操作    [blockOperation addDependency:lastBlockOperation];    [operationQueue addOperation:blockOperation];}
//将最后一个操作加入线程队列[operationQueue addOperation:lastBlockOperation];
串行队列
//主队列--串行dispatch_queue_t queue1 = dispatch_get_main_queue()
;//自定义串行队列dispatch_queue_t queue2 = dispatch_queue_create("test_queue", DISPATCH_QUEUE_SERIAL);
//主队列NSOperationQueue * queue1 = [NSOperationQueue mainQueue];
//自定义队列 -- 把并发改为1NSOperationQueue * queue2 =
[[NSOperationQueue alloc]init];queue2.maxConcurrentOperationCount = 1;
最大并发
// 创建队列组dispatch_group_t group = dispatch_group_create();
// 创建信号量,并且设置值为3dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);dispatch_queue_t queue = dispatch_get_global_queue
(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++
){    dispatch_semaphore_wait(semaphore, 
DISPATCH_TIME_FOREVER);    dispatch_group_async(group, queue, ^{        NSLog(@"%i",i);        sleep(2);      
  // 每次发送信号则semaphore会+1,        dispatch_semaphore_signal(semaphore);    });}
扫一扫加入本群
上一篇 下一篇

猜你喜欢

热点阅读