进程与线程:NSThread,GCD,NSOperation,Runloop

29 8月

IOS中通常一个APP就一个进程,绝大部分情况都是使用多线程进行通信。

线程分主线程其他线程,主线程在APP存活时一直存在,通常就是UI操作,所以也被称为UI线程。IOS最核心的需求就是要保证用户操作的流畅,所以可能会影响UI体验的动作,例如网络操作,读取本地I/O等,都应该放到其他线程里,避免影响主线程。

  • NSThread
  • GCD
  • NSOperation
  • Runloop

NSThread

用于创建线程,线程创建并执行完操作后会自动销毁:

initWithTarget方法创建线程
currentThread / mainThread属性获取当前线程 / 主线程
isMultiThreaded方法判断是否是主线程
threadPriority / setThreadPriority方法获取 / 设置线程优先级

// 在线程里请求网络图片
NSThread *downloadImageThread = [[NSThread alloc] initWithBlock:^{
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:item.picUrl]]];
    self.rightImageView.image = image;   // 会有警告,在非主线程中操作UI
}];
downloadImageThread.name = @"downloadImageThread";
[downloadImageThread start];

上面代码会有警告,因为在在非主线程中操作了UI,所以更推荐GCD。

GCD

GCD(Grand Central Dispatch)是个自动利用CPU的高性能多线程解决方案。能自动管理分配线程池。当我们的列表请求很多图片时,每个请求网络图片的地方都用NSThread申请线程,其实也是一种浪费。

使用GCD会将我们的网络图片请求视为任务,加入到任务队列。从预先申请好的线程池中用空闲线程执行队列任务。

GCD有三种队列:

1.主线程有对应的主队列

dispatch_get_main_queue(void)

2.非主线程有4个优先级的队列:High/Default/Low/Background

dispatch_get_global_queue(long identifier, unsigned long flags);

3.自定义队列,可以指定是串行还是并行

dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);

GCD会在合适的时机在线程池中为我们选择合适的线程来执行任务。执行代码方式有:

dispatch_async:异步执行
dispatch_sync:同步执行
dispatch_after:延迟xxx毫秒后执行
dispatch_once:保证在整个APP生命周期内只执行一次,常用于创建单例
dispatch_source:事件源,自定义触发和监听
dispatch_group:管理一组GCD操作
dispatch_barrier_async:并发队列中的同步点
dispatch_semaphore:信号量,用于线程间同步

dispatch_queue_global_t downloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  // 非主队列网络请求
dispatch_queue_main_t mainQueue = dispatch_get_main_queue();    // 主队列中更新UI

dispatch_async(downloadQueue, ^{
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:item.picUrl]]];
    dispatch_async(mainQueue, ^{
        self.rightImageView.image = image;
    });
});

NSOperation

可以看出GCD就是个block的封装,这在更复杂的场景下有点缺乏灵活性。IOS提供了NSOperation可以更灵活地支持线程间的切换,取消暂停任务等。

NSOperation相当于GCD更加面向对象的封装。可以将NSOperation类比成GCD里的block,NSOperationQueue类比成GCD里的队列。

Runloop

最后系统为每个线程都提供了Runloop,包括主线程。当我们不操作APP时,主线程因为有Runloop,所以线程不会被销毁而是进入睡眠等待。当用户有手势触发APP时,主线程会重新开始执行。

发表评论

电子邮件地址不会被公开。 必填项已用*标注