多线程以及底层实现

Posted LoSenTrad

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程以及底层实现相关的知识,希望对你有一定的参考价值。

一.概念

  • 什么是进程
    • 进程是指在系统中正在运行的一个应用程序
    • 每个进程之间的是独立的,每个进程均运行在其专用且受保护的内存空间内
    • 一个进程至少要有一个线程
  • 什么是线程
    • 一个线程要执行任务,必须得有线程
    • 一个进程(程序)的所有任务都在线程中执行的
    • 一个线程执行任务是串行的,也就是说一个线程,同一时间内,只能执行一个任务
  • 多线程原理
    • 同一时间,CPU只能处理1条线程,只有一条线程在工作(执行)
    • 多线程并发(同时)执行,其实质是CPU快速的在多线程之间调度(切换)
  • 如果线程过多,会怎样?
    • CPU在N多条线程中调度,会消耗大量的cpu资源
    • 每条线程被调度执行的频率越低(线程的执行效率低)
  • 多线程的优点
    • 能适当提高程序的执行效率
    • 能适当提高资源的利用率(CPU 内存利用率等)
  • 多线程的缺点
    • 创建线程是有开销的,ios下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间
    • 如果开启大量的线程,会降低程序的性能
    • 程序越多CPU的线程上的开销就越大
    • 程序设计更加复杂:线程之间的通讯,多线程的数据共享
  • 什么是主线程
    • 一个ios程序运行后,会开启一条线程,称为主线程,或者是UI线程
  • 主线程的主要作用
    • 显示和刷新UI界面
    • 处理UI事件(比如点击事件,滚动事件,拖拽事件等)
  • 主线程的使用注意
    • 别将比较耗时的操作放在主线程中,会导致UI界面的卡顿
    • 将耗时操作放在子线程(后台线程,非主线程)

二.多线程的4种方案

  • 1.C语言的POSIX接口:#include

+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程```
- 其他用法
  ```objc//创建NSThread对象
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:nil object:nil];
    //获取当前线程
    NSThread *thread1 = [NSThread currentThread];
    //设置线程的名字
    thread.name = @"这是设置线程名字的";
    [thread setName:@"这也是设置线程的名字的"];```
- 线程状态
![这里写图片描述](https://img-blog.csdn.net/20160615133614638)
- 互斥锁
 - @synchronized(锁对象)  // 需要锁定的代码  
注意:锁定1份代码只用1把锁(同个对象),用多把锁是无效的
 - 优点:能有效防止因多线程抢夺资源造成的额数据安全问题
 - 缺点:需要消耗大量的CPU资源
 - 使用前提:多线程同一时间抢夺同一块资源
 - 互斥锁就是使用了线程同步技术.就是多线程在按顺序执行
- OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁


###1.常用的方法
```objc
//在主线程中执行任务
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
//YES表示等Selector执行完后才执行后面
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//开辟一条子线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array 
//开辟一条子线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait 
//在后台执行任务(相当于开辟了一个子线程)
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg 
//类方法,开辟一条子线程执行任务
[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"hello"];




<div class="se-preview-section-delimiter"></div>

//开启子线程,并且要通过手动开启线程,否则无法执行任务的

// 创建线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"Alloc"];
    // 开启线程,将线程添加到线程可调度池里,等待CPU的调度
    [thread start];




<div class="se-preview-section-delimiter"></div>

//线程睡眠

//睡眠1秒
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
//线程睡眠1秒
[NSThread sleepForTimeInterval:1.0];




<div class="se-preview-section-delimiter"></div>

//线程退出,千万不要在主线程退出,否则程序就会出现各种问题,包括奔溃,黑屏,不能响应

[NSThread exit];




<div class="se-preview-section-delimiter"></div>

2.线程的取消

 // 创建线程--> 新建状态
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
    // 开启线程--> 将线程添加到可调度线程池中,等待CPU调度
    [thread start];
    // 睡
    [NSThread sleepForTimeInterval:0.2];
    // 取消线程执行,取消线程仅仅是给线程打上一个被取消的标记
    // 要实现真正取消线程的执行需要在线程内部的关键节点进行判断
    [thread cancel];
    NSLog(@"over");




<div class="se-preview-section-delimiter"></div>

3.线程的优先级

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:@"---"];
    thread.name = @"线程A";
    //设置线程的优先级,范围是0到1
    thread.threadPriority = 1.0;
    [thread start];




<div class="se-preview-section-delimiter"></div>

4.线程栈区的大小

// 不管主线程还是子线程,栈区大小默认都是512kb.
    //    NSLog(@"stackSize = %zd",[NSThread currentThread].stackSize / 1024);
    // 最小的栈区大小是16kb,必须是4的整数倍,建议不要修改栈区大小,使用默认即可.
    [NSThread currentThread].stackSize = 1024 * 1024;
    NSLog(
    @"stackSize = %zd",[NSThread currentThread].stackSize / 1024)




<div class="se-preview-section-delimiter"></div>

5.多线程访问数据导致的数据异常的处理

多个线程访问数据的时候会导致数据异常,这时候我们要使用互斥锁

// 先保证单条线程能正确工作:能够买完所有票
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 
    self.ticket = 20;
    // 创建线程
    NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    threadA.name = @"美女A";
    // 开启线程
    [threadA start];

    // 创建线程
    NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    threadB.name = @"美女B";
    // 开启线程
    [threadB start];

- (void)saleTicket 
    while (YES)
    
            // 睡
            [NSThread sleepForTimeInterval:1.0];
            // 使用互斥锁:可以保证锁住的代码同一时间只有一个线程访问.
            // 使用互斥锁锁住的代码要尽可能少,锁住关键节点的代码即可.
            // [[NSUserDefaults standardUserDefaults] synchronize];
            //@synchronized

           // self:锁对象,可以是任意NSObject类的对象.
           // 注意点:锁对象必须是所有线程对象能同时访问的对象.
           // 如果只有一个地方使用到互斥锁,一般锁对象就是self`,避免再创建一个对象.
           // NSObject *lockObj = [[NSObject alloc] init];
        @synchronized(self)
        
            // 判断是否有剩余的票数
            if(self.ticket > 0)
            
                // 如果有,则卖一张
                self.ticket --;
                NSLog(@"%@卖了一张票,剩余的票数:%zd",[NSThread currentThread].name, self.ticket);
                continue;
            
        
        // 没有票,则提示用户票没了
        NSLog(@"票没了");
        break;
    
    NSLog(@"over");





<div class="se-preview-section-delimiter"></div>

拓展:

•nonatomic 与 atomic
1)iOS中还有一种锁 原子锁atomic
2)nonatomic 非原子属性 (线程不安全),
3)atomic 原子属性,默认都是"原子"属性 (线程安全),也不能保证数据写入的正确性 4)原子属性,也是一个多线程技术,setter/getter函数是一个原子操作,如果多线程同时调用setter
时,不会出现某一个线程执行完setter所有语句之前,另一个线程就开始执行setter,相当于函数头尾 加了锁. 这样的话并发访问性能会比较低.
提示:设置原子属性后,不要自己去写原子属性的setter方法
原因:原子属性默认的setter方法中,使用了“128位自旋锁”,性能比互斥锁高,但是同样消耗性 能




<div class="se-preview-section-delimiter"></div>
  • ##3.C语言的GCD(性能好,代码更精简)
    • 什么是GCD
    • 全称是 Grand Central Dispatch,可译为“牛逼的中枢调度器”
    • 纯C语言,提供了非常多强大的函数
    • GCD的优势
    • GCD是苹果公司为多核的并行运算提出的解决方案
    • GCD会自动利用更多的CPU内核(比如双核、四核)
    • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
    • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
    • GCD的有四种队列:串行队列,并行队列,全局队列,主队列.
    • GCD有两种操作:异步操作,同步操作.
    • 异步操作dispatch_async,”会并发执行”,无法确定任务的执行顺序
    • 同步操作dispatch_sync,”会依次顺序执行”,能够决定任务的执行顺序
    • 主队列可以看成串行队列,如果在主线程中使用同步操作,那就会造成线程阻塞
    • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程,线程是操作
      系统可识别的最小执行和调度单位.

1.CGD的常用使用

注意:在主线程中,执行同步操作,里面的任务将不会执行(阻塞),同步异步决定是否可以开子线程,队列决定任务是怎么执行的.

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

//    [self gcdDemo1];//cgd的同步和异步执行任务.
//    [self gcdDemo2];
//    [self gcdDemo3];  

//cgd的同步和异步执行任务.
-(void)gcdDemo1

    void (^gcdBlock)() = ^()
    
        NSLog(@"这是简单的block,%@",[NSThread currentThread]);
    ;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_sync(queue, gcdBlock);//开启子线程
    dispatch_async(queue, gcdBlock);//不开启子线程

//gcd的常用写法(精简版)
- (void)gcdDemo2

    //在全局队列异步执行
    dispatch_async(dispatch_get_global_queue(0, 0), ^
        NSLog(@"async---%@",[NSThread currentThread]);
    );
    //在主队列中异步执行.(没有开启子线程)
    dispatch_async(dispatch_get_main_queue(), ^
        NSLog(@"async---%@",[NSThread currentThread]);
    );
    //在主队列同步执行
    dispatch_sync(dispatch_get_main_queue(), ^
        NSLog(@"sync---%@",[NSThread currentThread]);
    );

//线程间的通讯
- (void)gcdDemo3

    //在异步开启子线程
    dispatch_async(dispatch_get_global_queue(0, 0), ^
        NSLog(@"这是耗时任务---%@",[NSThread currentThread]);
        //异步在主线程执行任务
        dispatch_async(dispatch_get_main_queue(), ^
            NSLog(@"回到主线程刷新UI--%@",[NSThread currentThread]);
        );
    );





<div class="se-preview-section-delimiter"></div>

2.CGD中的串行队列

/// 串行队列异步执行
/// 提问:会不会开线程? 是否是顺序执行?
/// 问答:会开线程  开多条 是
- (void)gcdDemo3
    for (int i = 0; i < 10; i++) 
        // 参数1:队列的名称
        dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
        // 将任务添加到队列中,并指定执行任务的函数
        dispatch_async(queue, ^ 
            NSLog(@"%@--%d",[NSThread currentThread],i);
        );
    

/// 串行队列同步执行
/// 提问:会不会开线程? 是否是顺序执行?
/// 问答:不会开线程  是
- (void)gcdDemo1 
    // 创建串行队列
    // 参数1:队列的名称
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; ++i) 
        // 将任务添加到队列中,并指定执行任务的函数
        dispatch_sync(queue, ^ 
            NSLog(@"%@--%d",[NSThread currentThread],i);
        );
    

/// 串行队列异步执行
/// 提问:会不会开线程? 开几条线程? 是否是顺序执行?
/// 问答:会开线程  开1条   是  全对
- (void)gcdDemo2 
    // 创建串行队列
    // 参数1:队列的名称
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; ++i) 
        // 将任务添加到队列中,并指定执行任务的函数
        dispatch_async(queue, ^ 
            NSLog(@"%@--%d",[NSThread currentThread],i);
        );
    





<div class="se-preview-section-delimiter"></div>

3.GCD中的并发队列

/// 并发队列同步执行
/// 提问:会不会开线程? 开几条? 是否是顺序执行?
/// 答案:不会开  顺序
- (void)gcdDemo1 
    // 创建串行队列
    // 参数1:队列的名称
    for (int i = 0; i < 10; ++i) 
        // 将任务添加到队列中,并指定执行任务的函数
        dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
        dispatch_sync(queue, ^ 
            NSLog(@"%@--%d",[NSThread currentThread],i);
        );
    

/// 串行队列异步执行
/// 提问:会不会开线程? 开几条线程? 是否是顺序执行?
/// 问答:会开线程  不知道,由底层线程池决定   不是
- (void)gcdDemo2 
    // 创建串行队列
    // 参数1:队列的名称
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 5; ++i) 
        // 将任务添加到队列中,并指定执行任务的函数
        dispatch_async(queue, ^ 
            NSLog(@"%@--%d",[NSThread currentThread],i);
        );
    
    NSLog(@"下载xxxx=%@",[NSThread currentThread]);
    NSLog(@"下载B=%@",[NSThread currentThread]);
    dispatch_async(queue, ^
        NSLog(@"下载C=%@",[NSThread currentThread]);
    );
    dispatch_async(queue, ^
        NSLog(@"下载A=%@",[NSThread currentThread]);
    );





<div class="se-preview-section-delimiter"></div>

4.GCD中的主队列(特殊的串行队列)

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 
    NSLog(@"start");
    [self gcdDemo3];
    NSLog(@"end");


///主队列同步执行不死锁
- (void)gcdDemo3 
    // 获得主队列
    dispatch_queue_t queue =  dispatch_get_main_queue();
    dispatch_async(dispatch_get_global_queue(0, 0), ^
        NSLog(@"over = %@",[NSThread currentThread]);
        // 同步任务
        dispatch_sync(queue, ^
            NSLog(@"%@---%zd",[NSThread currentThread],100);
        );
    );


///主队列同步执行
- (void)gcdDemo2 
    // 获得主队列
    dispatch_queue_t queue =  dispatch_get_main_queue();
    // 同步任务
    // 主队列中的任务必须在主线程空闲的时才会执行.
    // 主队列中添加同步任务会造成死锁.
    dispatch_sync(queue, ^
        NSLog(@"%@---%zd",[NSThread currentThread],100);
    );
    NSLog(@"over");

//主队列异步执行
- (void)gcdDemo1 
    // 获得主队列
    dispatch_queue_t queue =  dispatch_get_main_queue();
    for (int i = 0; i < 5; ++i) 
        // 异步任务
        dispatch_async(queue, ^
            NSLog(@"%@---%zd",[NSThread currentThread],i);
        );
    





<div class="se-preview-section-delimiter"></div>

5.GCD中的全局队列

//全局队列异步执行
//全局队列特点跟并发队列是一样的
// dispatch_get_global_queue:获得全局队列不需要关心什么时候销毁
// 自己创建的并发队列需要在不使用的时候销毁
// 一般开发第三方框架时会是用自定义并发队列.
- (void)gcdDemo1 
    // 获得主队列
//    Flags that are reserved for future use. Always specify 0 for this parameter
    // 参数1:
    // 参数2:保留给未来使用,永远给个0
    dispatch_queue_t queue =  dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 10; ++i) 
        // 异步任务
        dispatch_async(queue, ^
            NSLog(@"%@---%zd",[NSThread currentThread],i);
        );
    
    // 在ARC中不允许调用release方法
//    dispatch_release(queue);





<div class="se-preview-section-delimiter"></div>

6.CGD中的dispatch_barrier_async,barrier异步的使用

使用 dispatch_barrier_async 添加的 block 会在之前添加的 block 全部运行结束之后,统一在同一个线程顺序执行,从而保证对非线程安全的对象进行正确的操作!

- (void)viewDidLoad 
    [super viewDidLoad];
    // 创建并发队列
    queue  = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
    // dispatch_barrier_async:一定是要使用自定义并发队列
//     queue  = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 20; ++i) 
        [self loadImage:i];
    

/// 加载index索引指定图片
// 主要用于在多个异步操作完成之后,统一对非线程安全的对象进行更新
- (void)loadImage:(NSInteger)index
    dispatch_async(queue, ^
        // 获得图片名称
        NSString *imageName = [NSString stringWithFormat:@"%zd.jpg",index % 9];
        // 获得图片路径
        NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:nil];
        // 加载图片
        UIImage *image = [UIImage imageWithContentsOfFile:filePath];
        NSLog(@"下载第%zd张图片,%@",index,[NSThread currentThread]);
        dispatch_barrier_async(queue, ^
            NSLog(@"第%ld张图片下载完成=%@,",(long)index,[NSThread currentThread]);
            // 将图片添加到数组中
            // NSMutableArray不是线程安全的类
            // NSMutableDictionary也不是线程安全的类的
            // 凡是带有mutable单词的类都不是线程安全.
            [self.images addObject:image];
        );
    );





<div class="se-preview-section-delimiter"></div>

7.dispatch_after操作和dispatch_once

1.dispatch_after

    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));                                 
    //延时操作,这里是开启了子线程的 
    dispatch_after(when, dispatch_get_global_queue(0, 0), ^
        NSLog(@"延迟操作是子线程:%@",[NSThread currentThread]);
        NSLog(@"这是延迟操作!!!");
    );   




<div class="se-preview-section-delimiter"></div>

2.dispatch_once

//使用dispatch_once实现单例,以及和互斥锁实现单例的比较

(dispatch_once实现的单例比互斥锁实现的单例效率高)

// dispatch_once 
// 1.要提供一个全局的访问点
// 2.在这个项目中,只会有一个实例对象
+(instancetype)sharedHttpTool 
    static id instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        // 在这里写的代码在整个项目运行过程中只会执行一次
        NSLog(@"是一次吗");
        instance = [[self alloc] init];
    );
    return instance;


// 使用互斥锁实现单例
+ (instancetype)sharedSync 
    static id instance = nil;
    @synchronized(self) 
        if (instance == nil) 
            instance = [[self alloc] init];
        
    
    return instance;





<div class="se-preview-section-delimiter"></div>

8.调度组

- (void)gcdDemo1 
    // 组对象
    dispatch_group_t group = dispatch_group_create();
    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"下载图片1=%@",[NSThread currentThread]);
    );
    dispatch_group_async(group, queue, ^
        [NSThread sleepForTimeInterval:1.5];
        NSLog(@"下载图片2=%@",[NSThread currentThread]);
    );
    dispatch_group_async(group, queue, ^
        [NSThread sleepForTimeInterval:2.5];
        NSLog(@"下载图片3=%@",[NSThread currentThread]);
    );
    // dispatch_group_notify:当组里面的所有任务执行完毕,会在主队列中添加任务.并执行任务
    dispatch_group_notify(group, dispatch_get_main_queue(), ^
         NSLog(@"更新UI %@",[NSThread currentThread]);
    );

    NSLog(@"come here");

//    dispatch_group_async(group, dispatch_get_main_queue(), ^
//        NSLog(@"更新UI %@",[NSThread currentThread]);;
//    );





<div class="se-preview-section-delimiter"></div>

9.dispatch_apple的使用

NSArray *arr = @[@1,@2,@3,@4,@5];
    CFTimeInterval begin1 = CFAbsoluteTimeGetCurrent();
    dispatch_apply(arr.count, dispatch_get_global_queue(0, 0), ^(size_t index)
    
        NSLog(@"%@,%@",arr[index],[NSThread currentThread]);

    );
    CFTimeInterval begin2 = CFAbsoluteTimeGetCurrent();
    NSLog(@"%lf",begin1 - begin2);





<div class="se-preview-section-delimiter"></div>
  • 4.Objective-C的NSOperation和NSOperationQueue(基于GCD)
    ###示例代码:
    1.用start添加任务和开启线程
//添加任务和线程的开启
- (void)opDemo1

    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
    //用start开启开启线程是在当前线程中执行任务的
    [op start];

- (void)test

    NSLog(@"it is test method-->%@",[NSThread currentThread]);





<div class="se-preview-section-delimiter"></div>

2.operation添加操作和开启线程

- (void)opDemo2

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //设置队列的并发数
    //如果设置为1,那么就变成了串行队列
    queue.maxConcurrentOperationCount = 2;
    for (int i = 0; i < 10; i ++) 
        //添加操作
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test:) object:nil];
        //往队列中添加操作就-->开启了异步(除了在mainQueue中不开启子线程)
         [queue addOperation:op];
    
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
        NSLog(@"这是block操作-->%@",[NSThread currentThread] );
    ];
    [[[NSOperationQueue alloc] init] addOperation:op];





<div class="se-preview-section-delimiter"></div>
 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
    ];

    // 添加额外的任务(在子线程执行)
    [op addExecutionBlock:^
        NSLog(@"下载2------%@", [NSThread currentThread]);
    ];

    [op addExecutionBlock:^
        NSLog(@"下载3------%@", [NSThread currentThread]);
    ];
    [op addExecutionBlock:^
        NSLog(@"下载4------%@", [NSThread currentThread]);
    ];

    [op start];

3.线程间通信

//线程间的通讯
- (void)opDemo3

    [[[NSOperationQueue alloc] init] addOperationWithBlock:^
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"这是耗时操作--%@",[NSThread currentThread]);
        [[NSOperationQueue mainQueue] addOperationWithBlock:^
            NSLog(@"UI更新--%@",[NSThread currentThread]);
        ];
    ];

4.队列的挂起,开启以及取消队列操作

//控制队列的挂起和开启的按钮点击事件
- (IBAction)pauseAndResume 
    if(self.queue.operationCount == 0) 
        NSLog(@"队列中没有操作");
        return;
    
    // 设置队列的挂起状态
    // setter  = !getter
    self.queue.suspended = !self.queue.isSuspended;
    // 挂起队列不会影响正在执行的操作
    // operationCount中包含没有执行完毕的所有操作.
    // 队列如果是挂起的,再往队列中添加操作,也不会执行
    if (self.queue.suspended) 
        NSLog(@"暂停 = %zd",self.queue.operationCount);
     else 
        NSLog(@"继续 = %zd",self.queue.operationCount);
    

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 
    for (int i = 0; i < 20; ++i)
    
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"%@--%d", [NSThread currentThread],i);
        ];
        [self.queue addOperation:op];
    

- (NSOperationQueue *)queue 
    if (_queue == nil) 
        _queue = [[NSOperationQueue alloc] init];
        // 设置最大并发数
        _queue.maxConcurrentOperationCount = 2;
    
    return _queue;

5.任务之间添加依赖

- (void)op1

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *bop = [NSBlockOperation blockOperationWithBlock:^
        NSLog(@"111bop,%@",[NSThread currentThread]);
    ];
    //往bop任务中添加额外的任务
    [bop addExecutionBlock:^
        NSLog(@"2222---%@",[NSThread currentThread]);
    ];
    NSInvocationOperation *iop = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top) object:nil];
    NSInvocationOperation *iop2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top2) object:nil];
    //必须先添加依赖,再把任务添加到队列中
    [bop addDependency:iop];
    [iop addDependency:iop2];
    [queue addOperation:bop];
    //添加任务到队列中
    [queue addOperation:iop];
    [queue addOperation:iop2];  

以上是关于多线程以及底层实现的主要内容,如果未能解决你的问题,请参考以下文章

多线程以及底层实现

Java多线程

多线程七 AQS

深入ThreadLocal的底层实现机制以及对应的使用风险

并发编程艺术-锁类型以及底层原理

iOS-多线程的底层实现