IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS 主线程和主队列 -> 正文阅读

[移动开发]iOS 主线程和主队列

先说结论,?主队列的任务只在主线程中被执行的,而主线程运行的是一个 runloop,不仅仅只有主队列的中的任务,还会处理 UI 的布局和绘制任务, 同时还可能会有其他队列中的任务。

举个例子

问题1: 主线程是否只做主队列的事情?

- (void)someMethod {
    dispatch_queue_t queue = dispatch_queue_create("com.kk", nil);
    dispatch_sync(queue, ^{
        NSLog(@"current thread = %@, curren queue = %@, main queue = %@",
              [NSThread currentThread],
              [NSString stringWithCString:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) encoding:NSUTF8StringEncoding],
              [NSString stringWithCString:dispatch_queue_get_label(dispatch_get_main_queue()) encoding:NSUTF8StringEncoding]);
        ?});
}


------------------

current thread = <NSThread: 0x600002c0cf00>{number = 1, name = main}, 
curren queue = com.kk, 
main queue = com.apple.main-thread

结论: 这里是主线程但不是主队列, 主线程也做了其他队列的任务。

主线程还有可能会执行其他队列的任务。这是为了避免线程切换对性能的消耗。因为CPU的调度多线程势必会不断切换上下文。这样每个线程需要一个上下文来记录当前执行状态。这样新线程被执行时首先将上下文写入寄存器,执行结束寄存器重新写入上下文,如此不断切换才避免了多线程的数据混乱。

问题2: 主队列任务是否一定在主线程执行?

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // dispatch_sync和用dispatch_async , 一样都会回到主线程
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"%@",[NSThread currentThread]);
            NSLog(@"[NSThread isMainThread] - %d",[NSThread isMainThread]);
        });
    });

------------------

//  <_NSMainThread: 0x6000008980c0>{number = 1, name = main}
// [NSThread isMainThread] - 1

结论:所有主队列任务只会在主线程中执行,

  • 如果在子线程调用dispatch_sync(dispatch_get_main_queue() 或者?dispatch_async(dispatch_get_main_queue() , 会回到主线程, 保证主队列的任务在主线程中执行
  • 如果在主线程使用dispatch_sync(dispatch_get_main_queue() 会死锁, block内的内容不会执行, 程序卡死
  • 如果在主线程使用dispatch_async(dispatch_get_main_queue() 在下一个主线程的runloop中执行
  • 所以结合所有情况,所有主队列的任务只会在主线程中执行。

主线程判断的几个方法

下面这几种切换到主线程执行的方法,有什么优缺点?

  • 方法1
    if ([NSThread isMainThread]) {
        //xxx
    } else {
        dispatch_async(dispatch_get_main_queue(), ^{
            //xxx
        });
    }

使用sync/async回到主线程,大部分都是这么用的,也没有什么问题, 下面会说到一种特殊情况
  • 方法2
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});

始终异步放在下一个loop使用,会延迟执行时机, 等到下一个runloop才会执行
  • 方法3
    if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())) {
        //xxx
    } else {
        dispatch_async(dispatch_get_main_queue(), ^{
            //xxx
        });
    }

通过判断当前队列label的形式,可以保证任务一定是放在主队列中的。
上面已经说过, 主队列的任务一定是在主线程执行的.

综合来说,方法3是更好的判断是否是主线程的方式。

为什么推荐方法3

队列不是线程,从Apple的GCD guide中了解到,不能保证调度队列将在单独的线程上执行。 GCD将确定哪个线程,并在必要时创建一个新线程。?

查阅了sdwebimage 3.8版本和 4.4.2版本,发现了两种不同的写法

3.8版本

#define dispatch_main_sync_safe(block)\
    if ([NSThread isMainThread]) {\
        block();\
    } else {\
        dispatch_sync(dispatch_get_main_queue(), block);\
    }

#define dispatch_main_async_safe(block)\
    if ([NSThread isMainThread]) {\
        block();\
    } else {\
        dispatch_async(dispatch_get_main_queue(), block);\
    }

4.4.2版本

#ifndef dispatch_queue_async_safe
#define dispatch_queue_async_safe(queue, block)\
    if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(queue)) {\
        block();\
    } else {\
        dispatch_async(queue, block);\
    }
#endif

#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block) dispatch_queue_async_safe(dispatch_get_main_queue(), block)
#endif

那么到底上面两个版本哪个版本才是最安全的呢?

既然SDWebImage最新版本换了方式,那么肯定,4.2.2是最安全的。

那SDWebImage为什么要升级呢?

在查阅一阵子资料之后,发现在 ReactiveCocoa 的一个 issue里提到在MapKit 中的 MKMapView 有个 addOverlay 方法,这个方法不仅要在主线程中执行,而且要把这个操作加到主队列中才可以。并且后来 Apple DTS 也承认了这是一个bug

由此,我们得想一个更加安全的方式规避这种即使有此类bug,万一别的API也有这样的问题也不至于导致APP出问题.

我们知道,在主队列中的任务,一定会放到主线程执行; 所以只要是在主队列中的任务,既可以保证在主队列,也可以保证在主线程中执行。所以咱们就可以通过判断当前队列是不是主队列来代替判断当前执行任务的线程是否是主线程,这样更加安全!

所以更推荐使用方案3来作为是不是主线程的判断.

参考文章:?

主队列&主线程

在主线程执行_主线程和主队列的关系

『ios』主线程 和 主队列的关系,绝对安全的UI操作,主线程中一定是主队列?

iOS UI 操作在主线程不一定安全?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-04 13:32:50  更:2022-01-04 13:33:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:19:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码