iOS - Multi-Thread

2019-05-21  本文已影响0人  ienos

概念篇

进程

正在进行的程序

线程

1. 一个进程至少包括一个线程,线程就是程序的执行流。程序执行任务的最小调度单位。
2. 程序启动时,会自动创建主线程。
3. 更新 UI 在主线程中执行

多线程

一个程序中有多条执行路径。在 iOS 中一些比较耗时的操作放在另外一条执行路径里,让它与主线程同时运行,这样不会造成主线程的阻塞。

单核多线程 & 多核多线程

1. 单核多线程:比单一主线程执行时间多,但是能防止卡顿。在多个线程之间进行切换
2. 多核多线程:多个 CPU 同时处理多个线程

并行 & 并发

1. 并发:在单核 CPU 以分时的方式,进行上下文切换,达到任务同时运行的效果
2. 并行:在多核 CPU 上真正意义上的多任务同时运行

同步 & 异步

1. 同步:优先级高,在线程中有执行顺序,不会开启新的线程
2. 异步:优先级低,在线程中没有执行顺序,开启新的线程,开启几条线程由队列决定。

队列

1. 管理任务在哪些线程中执行,FIFO(先进先出)
2. 主队列是串行队列

队列 & 任务 的执行方式

1. 同步不具备开启线程的能力,无论什么队列都不会开启线程
2. 异步函数具备开启线程的能力,开启几条线程由队列决定

Q:主队列中开启同步任务为什么会阻塞线程?

在主队列中开启同步任务,因为主队列是串行队列,里面的线程是有顺序的,先执行完一个线程才执行下一个线程,而主队列始终只有一个主线程,主线程是不会执行完毕的,因为他是无线循环的,除非关闭应用程序,因此在主线程开启一个同步任务,同步任务会抢占执行的资源,而主线程任务一直执行某些操作,不肯放手。两个的优先级都很高,最终导致死锁,阻塞线程。
   NSLog(@"执行任务1")
   dispatch_queue_t queue = dispatch_get_main_queue();
   dispatch_sync(queue, ^{
       NSLog(@"执行任务2")
   });
   NSLog(@"执行任务3")

// 主队列正在进行任务
// 执行任务 1
// 主队列加入同步任务
// 主队列会等待主线程执行完再执行同步任务
// 但是主线程执行完毕需要等待同步任务执行完毕
// 造成死锁

线程安全篇

多个线程可能会访问同一块资源,很容易引发 数据错乱 和 数据安全 问题。

解决方法

@synchronized(锁对象){
// 需要锁定的代码
} // 锁一份代码只用一把锁

尽量将加锁、资源抢夺的业务逻辑交给服务端处理,减小移动端的压力。

优缺点

原子性 和 非原子性

nonatomic 和 atomic 两种选择

Q:使用 atomic 一定是线程安全吗?

 atomic 只是在 setter 方法加了锁,防止多线程一直去写 @property; 假设 ABC 在写 @property,D 在读取,D 会读到一些随机值 (ABC 修改的值),这就不是线程安全了。

线程通讯

NSOject

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;// 主线程通讯
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; // 指定线程通讯

延时执行 - NSObject

[self performSelector:@selector(run) withObject:nil afterDelay:2.0];

GCD

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒后异步执行这里的代码...
});
 //2.计算任务执行的时间
dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
//3.会在when这个时间点,执行queue中的这个任务
dispatch_after(when, queue1, ^{
 NSLog(@"并发队列-延迟执行------%@",[NSThread currentThread]);
 });

线程使用篇

NSThread

API
  1. 获取当前线程
[NSThread currentThread]
  1. 判断是否为主线程
[thread isMainThead]
  1. 线程暂停一段时间(线程阻塞,线程被移除可调度线程池,此时不可调度)
[NSThread sleepForTimeInterval:200];
//  [NSThread sleepUntilDate:date];
  1. 退出线程(线程死亡(任务结束、发生异常、强制退出),从内存中移除)
[NSThread exit];
实现多线程方法
  1. 创建一个 NSThread 对象
(1)[NSThread detachNewThreadSelector:@select(thread1:) toTarget:self withObject: @"类方法实现多线程"]
(2)[self performSelectorInBackground:@selector(run) withObject:nil];
(3) NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(thread:) object:IMAGE_BEAUTY];
//  前两种方法线程自动执行,简单快捷,但是无法对线程进行更详细的设置,第三种方法需要 start 手动开启,可以在 start 前添加一些设置,比如优先级
  1. 添加 NSThread 标识
thread.name = @"美女";
  1. 设置线程执行的优先级(提高优先级执行概率)从 0-1(默认0.5)
// cpu在多线程之间切换, 并不能决定哪个线程先执行
thread.threadPriority = 0.9;
  1. 开始执行子线程
[thread start];
  1. 子线程执行代码
- (void)thread:(id)object {
    // 开辟子线程的时候就需要添加自动释放池@autoreleasepool {
    // 设置下载参数
    NSURL *url = [[NSURL alloc]initWithString:object];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data]; // 回到主线程更新 UI
    [self performSelectorOnMainThread:@selector(showImageOnMainThread:) withObject:image waitUntilDone:YES];
    }
}

GCD

Grand Central Dispath 是 Apple 开发的一个多核编程的解决方法,基于 C 的 API.
GCD 是 libdispath 的市场名称,而 libdispath 作为 Apple 的一个库,为并发代码在多核硬件上提供有力支持。
GCD 的优势
1. GCD 是苹果公司为多核的并行运算提出的解决方案。
2. GCD 会自动利用更多的内核(如:双核 和 四核)
3. GCD 会自动管理线程的声明周期(创建线程、调度任务、销毁线程)
4. 程序员只需要告诉 GCD 想要执行的任务,不需要编写任何线程管理代码
GCD 执行过程
定制执行的任务,然后添加到对应的操作队列, GCD 将会自动将队列中的任务取出,放到对应的线程中执行。
dispath_sync(dispath_queue_t queue, dispath_block_t block)// queue 队列,block 任务
dispath_async(dispath_queue_t queue, dispath_block_t block)// queue 队列,block 任务
队列的创建
dispath_queue_t dispath_queue_creat(const char *label, dispath_queue_attr_t attr);// label 队列名称,attr 队列属性,一般用 NULL
// 非 ARC 下需要手动释放手动创建的队列
dispath_release(queue);

凡是函数中带有 creat/copy/new/retain ,都需要在不需要使用这个数据的时候进行 release
GCD 的数据类型在 ARC 的环境下不需要再做 release
CF (core Foundation) 的数据类型在 ARC 环境下还是需要 release
获取主队列(串行队列)

dispath_get_main_queue()
dispath_get_global_queue(dispath_queue_priority_t priority, unsigned long flags); // priority 设置优先级,flag 参数暂时不用,传 0

全局并发队列的优先级

#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)

#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
dispatch_queue_t urls_queue = dispatch_queue_create("bloh.devtang.com",NULL);
dispatch_async(urls_queue, ^{
    // your code
});
dispatch_release(urls_queue);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(0,0),^{
    // 并行执行的线程一
    dispatch_group_leave(group);// 异步返回标识
});
dispatch_group_async(group, dispatch_get_global_queue(0,0),^{
    // 并行执行的线程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0),^{
    // 汇总结果
    // dispatch_group_enter dispatch_group_leave 如果没有添加这一对的话,异步还没返回就会调 notify
});
常见应用
dispatch_async(dispatch_get_global_queue(0,0),^{
    // something
});
dispatch_async(dispatch_get_main_queue(),^{
    // something
});
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
    // code to be executed once
});
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispath_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime,dispatch_get_main_main_queue(),^(void){
    // code to be executed on the main queue after delay
});
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_signal(semaphore);
    });
      dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_signal(semaphore);
    });

    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_signal(semaphore);
    });
上一篇下一篇

猜你喜欢

热点阅读