OC线程常用方法记录一下

1.NSThread
每个NSThread对象对应一个线程,真正最原始的线程。
1)优点:NSThread 轻量级最低,相对简单。
2)缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等。

2.NSOperation
自带线程管理的抽象类。
1)优点:自带线程周期管理,操作上可更注重自己逻辑。
2)缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。

3.GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。
1)优点:最高效,避开并发陷阱。
2)缺点:基于C实现。

4.选择小结
1)简单而安全的选择NSOperation实现多线程即可。
2)处理大量并发数据,又追求性能效率的选择GCD。
3)在频繁使用多线程的程序中一般不建议使用NSThread

1、自定义串行队列
// 串行队列 SERIAL
dispatch_queue_t queue = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);

    // 防止循环引用 使用 __weak 修饰
    __weak typeof(self)weakSelf = self;
    // 异步任务 分线程中执行 不会阻塞主线程
    dispatch_async(queue, ^{
        [weakSelf eat];
    });
   // 同步任务 主线程中执行 会阻塞当前线程
    dispatch_sync(queue, ^{        
        [weakSelf eat];
    });
2、自定义并行队列
// 并行队列 CONCURRENT
dispatch_queue_t queue2 = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue2, ^{
    [weakSelf eat];
});
dispatch_sync(queue2, ^{
    [weakSelf eat];
});
3、获取系统的4个并行队列
    dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue4, ^{
        [weakSelf eat];
    });
4、子线程中执行完之后,回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
     // 更新UI
});
5、所有任务执行完成后回调(执行任务为同步请求)
   dispatch_group_t group = dispatch_group_create();
   dispatch_group_async(group,queue2,^{执行任务A});
   dispatch_group_async(group,queue2,^{执行任务B});
   dispatch_group_async(group,queue2,^{执行任务C});
   dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"queue2的所有任务都执行完了");
   });
6、网络异步请求--(利用group)所有任务执行完成后回调
@property (nonatomic, strong, nullable) dispatch_group_t serviceGroup;
[SVProgressHUD showModalLoading];
//(重复调用会出现dispatch_group相关的巨坑,因此要判断group是否为空)
if (self.serviceGroup == nil) {
    dispatch_group_t group = dispatch_group_create();
    self.serviceGroup = group;
}
[self loadData];//加载数据
dispatch_group_notify(self.serviceGroup, dispatch_get_main_queue(), ^{
        [SVProgressHUD dismissModalLoadingView];
});
- (void)loadData {
    WS(weakSelf);
    dispatch_group_enter(self.serviceGroup);
    NSMutableDictionary *parameter = [NSMutableDictionary dictionary];
    parameter[@""] = @"";
    [[NetWorkingManager shareManager] GET:@"" parameters:parameter progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        dispatch_group_leave(weakSelf.serviceGroup);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        
    }];
}
7、网络异步请求--(利用信号量)按顺序执行
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self loadDataWithSemaphore:semaphore];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [self loadDataWithSemaphore:semaphore];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [self loadData3];
});

- (void)loadDataWithSemaphore:(dispatch_semaphore_t)semaphore {
    WS(weakSelf);
    NSMutableDictionary *parameter = [NSMutableDictionary dictionary];
    parameter[@""] = @"";
    [[NetWorkingManager shareManager] GET:@"" parameters:parameter progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        
    }];
}
//信号量-->dispatch_semaphore_create 
  https://www.jianshu.com/p/73470d7de082
8、GCD栅栏函数
这里我们用到栅栏函数dispatch_barrier_(a)sync,(也可以用队列组),我们要注意的是不能使用全局并发队列(系统提供给我们的)否则会散失栅栏函数的意义
针对dispatch_barrier_sync 如果你传入的是串行或全局并发队列 则它的作用和 dispatch_sync 一样; 如果是 dispatch_barrier_async 则它的作用和 dispatch_async一样

区别:先看官方文档
dispatch_barrier_sync: Submits a barrier block object for execution and waits until that block completes.(提交一个栅栏函数在执行中,它会等待栅栏函数执行完)
我的理解:阻塞 queue,不同的是 这个方法还会 阻塞当前线程。

dispatch_barrier_async: Submits a barrier block for asynchronous execution and returns immediately.(提交一个栅栏函数在异步执行中,它会立马返回)
我的理解:这个方法会阻塞这个 queue(注意是阻塞 queue ,而不是阻塞当前线程)

1.先来看看dispatch_barrier_sync
- (void)test {
    //创建一个自定义并发队列
    NSLog(@"start");
    dispatch_queue_t queue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
    //异步函数 无序执行
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 1", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 2", [NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        NSLog(@"+++++barrier+++++");
    });
    NSLog(@"红成魔");
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 3", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 4", [NSThread currentThread]);
    });
    NSLog(@"last");
}
打印:
2021-04-02 14:47:27.971390+0800 343[9604:230587] start
2021-04-02 14:47:27.971691+0800 343[9604:230736] <NSThread: 0x600003fd63c0>{number = 5, name = (null)} -- 1
2021-04-02 14:47:27.971694+0800 343[9604:230741] <NSThread: 0x600003f95dc0>{number = 6, name = (null)} -- 2
2021-04-02 14:47:27.971879+0800 343[9604:230587] +++++barrier+++++
2021-04-02 14:47:27.972009+0800 343[9604:230587] 红成魔
2021-04-02 14:47:27.972133+0800 343[9604:230587] last
2021-04-02 14:47:27.972187+0800 343[9604:230736] <NSThread: 0x600003fd63c0>{number = 5, name = (null)} -- 3
2021-04-02 14:47:27.972222+0800 343[9604:230741] <NSThread: 0x600003f95dc0>{number = 6, name = (null)} -- 4
再次打印:
2021-04-02 14:48:53.684541+0800 343[9624:231947] start
2021-04-02 14:48:53.684813+0800 343[9624:232018] <NSThread: 0x60000076cd40>{number = 5, name = (null)} -- 2
2021-04-02 14:48:53.684818+0800 343[9624:232016] <NSThread: 0x60000076cd00>{number = 4, name = (null)} -- 1
2021-04-02 14:48:53.685010+0800 343[9624:231947] +++++barrier+++++
2021-04-02 14:48:53.685136+0800 343[9624:231947] 红成魔
2021-04-02 14:48:53.685267+0800 343[9624:231947] last
2021-04-02 14:48:53.685370+0800 343[9624:232016] <NSThread: 0x60000076cd00>{number = 4, name = (null)} -- 4
2021-04-02 14:48:53.685371+0800 343[9624:232018] <NSThread: 0x60000076cd40>{number = 5, name = (null)} -- 3

2.换成dispatch_barrier_async
- (void)test {
    //创建一个自定义并发队列
    NSLog(@"start");
    dispatch_queue_t queue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
    //异步函数 无序执行
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 1", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 2", [NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"+++++barrier+++++");
    });
    NSLog(@"红成魔");
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 3", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@ -- 4", [NSThread currentThread]);
    });
    NSLog(@"last");
}

执行:
2021-04-02 14:50:46.552643+0800 343[9654:233393] start
2021-04-02 14:50:46.552857+0800 343[9654:233393] 红成魔
2021-04-02 14:50:46.552908+0800 343[9654:233479] <NSThread: 0x600000912c00>{number = 3, name = (null)} -- 1
2021-04-02 14:50:46.553001+0800 343[9654:233393] last
2021-04-02 14:50:46.553004+0800 343[9654:233478] <NSThread: 0x600000911dc0>{number = 6, name = (null)} -- 2
2021-04-02 14:50:46.553180+0800 343[9654:233478] +++++barrier+++++
2021-04-02 14:50:46.553348+0800 343[9654:233478] <NSThread: 0x600000911dc0>{number = 6, name = (null)} -- 3
2021-04-02 14:50:46.553373+0800 343[9654:233479] <NSThread: 0x600000912c00>{number = 3, name = (null)} -- 4

再次执行:
2021-04-02 14:51:46.928775+0800 343[9662:234265] start
2021-04-02 14:51:46.929065+0800 343[9662:234265] 红成魔
2021-04-02 14:51:46.929210+0800 343[9662:234265] last
2021-04-02 14:51:46.929162+0800 343[9662:234331] <NSThread: 0x600001fd8bc0>{number = 7, name = (null)} -- 1
2021-04-02 14:51:46.929277+0800 343[9662:234337] <NSThread: 0x600001fd57c0>{number = 6, name = (null)} -- 2
2021-04-02 14:51:46.929812+0800 343[9662:234337] +++++barrier+++++
2021-04-02 14:51:46.930106+0800 343[9662:234337] <NSThread: 0x600001fd57c0>{number = 6, name = (null)} -- 3
2021-04-02 14:51:46.930148+0800 343[9662:234331] <NSThread: 0x600001fd8bc0>{number = 7, name = (null)} -- 4

9、线程同步
(https://www.jianshu.com/p/4c4c5d3d4c10)
iOS线程同步方案性能从高到低排序,如下所示:
1、os_unfair_lock
os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持,从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等状态
2、OSSpinLock
OSSpinLock属于”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源,目前已经不再安全,可能会出现优先级反转问题:如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
3、dispatch_semaphore
semaphore叫做”信号量”
信号量的初始值,可以用来控制线程并发访问的最大数量;
假设信号量的初始值为1,就代表同时只允许一条线程访问资源
4、pthread_mutex
mutex属于”互斥锁”,等待锁的线程会处于休眠状态
5、dispatch_queue(DISPATCH_QUEUE_SERIAL)
使用GCD的串行队列,实现线程同步
6、NSLock
NSLock是对mutex普通锁的封装,NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致,NSCondition是对mutex和cond的封装,NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值
7、NSCondition
8、pthread_mutex(recursive)
9、NSRecursiveLock
10、NSConditionLock
11、@synchronized
@synchronized是对mutex递归锁的封装,源码查看:objc4中的objc-sync.mm文件
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作

iOS中的读写安全方案(https://www.jianshu.com/p/4c4c5d3d4c10)
1、pthread_rwlock:读写锁
2、dispatch_barrier_async:异步栅栏调用


所谓线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全问题,所采取的一种措施。当然也有很多实现方法,请往下看:
互斥锁 :给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。
OBJECTIVE-C
@synchronized(self) {
  //需要执行的代码块
}
SWIFT
objc_sync_enter(self)
//需要执行的代码块
objc_sync_exit(self)

同步执行 :我们可以使用多线程的知识,把多个线程都要执行此段代码添加到同一个串行队列,这样就实现了线程同步的概念。当然这里可以使用 GCD 和 NSOperation 两种方案,我都写出来。
//GCD
//需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
dispatch_sync(queue, ^{
    NSInteger ticket = lastTicket;
    [NSThread sleepForTimeInterval:0.1];
    NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
    ticket -= 1;
    lastTicket = ticket;
});
//NSOperation & NSOperationQueue
//重点:1. 全局的 NSOperationQueue, 所有的操作添加到同一个queue中
//       2. 设置 queue 的 maxConcurrentOperationCount 为 1
//       3. 如果后续操作需要Block中的结果,就需要调用每个操作的waitUntilFinished,阻塞当前线程,一直等到当前操作完成,才允许执行后面的。waitUntilFinished 要在添加到队列之后!
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSInteger ticket = lastTicket;
    [NSThread sleepForTimeInterval:1];
    NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
    ticket -= 1;
    lastTicket = ticket;
}];
[queue addOperation:operation];
[operation waitUntilFinished];
//后续要做的事

延迟执行
所谓延迟执行就是延时一段时间再执行某段代码。下面说一些常用方法。

OBJECTIVE-C
// 3秒后自动调用self的run:方法,并且传递参数:@"abc"
[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];

SWIFT
之前就已经说过,Swift 里去掉了这个方法。

GCD
可以使用 GCD 中的 dispatch_after 方法,OC 和 Swift 都可以使用,这里只写 OC 的,Swift 的是一样的。
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 设置延时,单位秒
double delay = 3; 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
  // 3秒后需要执行的任务
});

NSTimer
NSTimer 是iOS中的一个计时器类,除了延迟执行还有很多用法,不过这里直说延迟执行的用法。同样只写 OC 版的,Swift 也是相同的。
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容