汝真的学明白。举例用Pthreads创建一个线程去实施一个职责。

从业时有发生得来因为,今天自怀念跟您聊聊线程的由来就是——当然是对一个共产党人的思想觉悟,为萌透析生命,讲解你正蒙圈的知识点,或者想排除脑袋才意识这么概括的技术方案。

成百上千口效仿线程,迷迷糊糊;很多丁问线程,有所指望;也来成千上万总人口形容线程,分享认知给在竭力的小伙,呦,呦,呦呦。但是,你真了解线程么?你真的会因此多线程么?你实在学明白,问清楚,写清楚了么?不管您掌握不懂得,反正自己不知道,但是,没照,你看了,你就是知晓了。


线程对操作系统来说就是平等截代码和运行时数据.操作系统回味每个线程保存相关的数目,当属接来自CPU的时间片中断事件频仍,就见面照自然规则从这些线程中精选一个,恢复她的运作时数,这样CPU就得继续执行这个线程了,也就是是单核CPU并不曾辙落实真正意义上之起执行,只是CPU快速地当多长线程之间调度,CPU调度线程的年华足够快,
就造成了大多线程并发执行的假象.并且就单核CPU而言多线程可以解决线程阻塞的题目,
但是那个本人运行效率并没有增长, 多CPU的彼此运算才真正解决了运行效率问题.
我们常用的线程
Pthreads

前言

  • 干线程,那即便只好提CPU,现代之CPU有一个不胜要紧之特点,就是时刻片,每一个赢得CPU的职责只能运行一个年华片规定之日子。
  • 实在线程对操作系统来说就是同等段子代码和运行时数。操作系统会为每个线程保存有关的多少,当属接来自CPU的时间片中断事件频仍,就见面照一定规则从这些线程中选取一个,恢复她的运行时数,这样CPU就可继续执行这个线程了。
  • 为即是实际上就是单核CUP而言,并从未艺术落实真正含义及的出现执行,只是CPU快速地于差不多条线程之间调度,CPU调度线程的流年足够快,就造成了差不多线程并发执行之假象。并且即使单核CPU而言多线程可以化解线程阻塞的问题,但是那自运行效率并无加强,多CPU的相互运算才真的化解了运行效率问题。
  • 系面临正运作的各级一个应用程序都是一个历程,每个过程系统还见面分配为她独立的内存运行。也就是说,在iOS系统被受,每一个行使都是一个历程。
  • 一个经过的有所任务还当线程中开展,因此每个过程至少要有一个线程,也就是主线程。那多线程其实就算是一个经过被多长长的线程,让具备任务并发执行。
  • 大抵线程在肯定意义上落实了经过内之资源共享,以及效率的升级换代。同时,在必程度达针锋相对独立,它是程序执行流的极其小单元,是经过遭到之一个实体,是执行顺序太基本的单元,有友好栈和寄存器。
  • 地方这些公是免是都亮,但是自己偏偏要说,哦呵呵。既然我们聊线程,那咱们便优先打线程开刀。

POSIX线程(POSIX threads), 简称Pthreads, 是线程的POSIX标准. 该标准定义了创建和操纵线程的一整套API. 在类Unix操作系统(Unix, Linux, Mac OS X等)中, 都使用Pthreads作为操作系统的线程.虽然高大上跨平台,但看似牛逼却基本用不到

Pthreads && NSThread

先行来拘禁与线程有太直接涉及的一样套C的API:

举例用Pthreads创建一个线程去实施一个任务:

Pthreads

POSIX线程(POSIX
threads),简称Pthreads,是线程的POSIX标准。该标准定义了创及操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac
OS X等)中,都应用Pthreads作为操作系统的线程。

#import "pthread.h"
- (void)pthreadsDoTask {
/*
     pthread_t:线程指针
     pthread_attr_t:线程属性
     pthread_mutex_t:互斥对象
     pthread_mutexattr_t:互斥属性对象
     pthread_cond_t:条件变量
     pthread_condattr_t:条件属性对象
     pthread_key_t:线程数据键
     pthread_rwlock_t:读写锁
     //
     pthread_create():创建一个线程
     pthread_exit():终止当前线程
     pthread_cancel():中断另外一个线程的运行
     pthread_join():阻塞当前的线程,直到另外一个线程运行结束
     pthread_attr_init():初始化线程的属性
     pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
     pthread_attr_getdetachstate():获取脱离状态的属性
     pthread_attr_destroy():删除线程的属性
     pthread_kill():向线程发送一个信号
     pthread_equal(): 对两个线程的线程标识号进行比较
     pthread_detach(): 分离线程
     pthread_self(): 查询线程自身线程标识号
     //
     *创建线程
     int pthread_create(pthread_t _Nullable * _Nonnull __restrict, //指向新建线程标识符的指针
     const pthread_attr_t * _Nullable __restrict,  //设置线程属性。默认值NULL。
     void * _Nullable (* _Nonnull)(void * _Nullable),  //该线程运行函数的地址
     void * _Nullable __restrict);  //运行函数所需的参数
     *返回值:
     *若线程创建成功,则返回0
     *若线程创建失败,则返回出错编号
     */
   pthread_t thread = NULL;
   NSString *params = @"Hello World";
   int result = pthread_create(&thread, NULL, threadTask), (__bridge void *)(params));
   result == 0 ? NSLog(@"creat thread success") : NSLog(@"creat thread failure");
// 设置子线程的状态为detached, 则该线程运行结束后会自动释放所有资源
pthread_detach(thread);
}
void *threadTask(void *params) {
     NSLog(@"%@ - %@", [NSThread currentThread], (__bridge NSString *)(params));
    return NULL;
}
震古烁今上有木有,跨平台有木有,你莫因此过起麻痹有!下面我们来拘禁一下此类似牛逼但的确基本用非顶之Pthreads凡怎用底:

与其说我们来用Pthreads开创一个线程去实施一个职责:

记得引入头文件`#import "pthread.h"`

-(void)pthreadsDoTask{
    /*
     pthread_t:线程指针
     pthread_attr_t:线程属性
     pthread_mutex_t:互斥对象
     pthread_mutexattr_t:互斥属性对象
     pthread_cond_t:条件变量
     pthread_condattr_t:条件属性对象
     pthread_key_t:线程数据键
     pthread_rwlock_t:读写锁
     //
     pthread_create():创建一个线程
     pthread_exit():终止当前线程
     pthread_cancel():中断另外一个线程的运行
     pthread_join():阻塞当前的线程,直到另外一个线程运行结束
     pthread_attr_init():初始化线程的属性
     pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
     pthread_attr_getdetachstate():获取脱离状态的属性
     pthread_attr_destroy():删除线程的属性
     pthread_kill():向线程发送一个信号
     pthread_equal(): 对两个线程的线程标识号进行比较
     pthread_detach(): 分离线程
     pthread_self(): 查询线程自身线程标识号
     //
     *创建线程
     int pthread_create(pthread_t _Nullable * _Nonnull __restrict, //指向新建线程标识符的指针
     const pthread_attr_t * _Nullable __restrict,  //设置线程属性。默认值NULL。
     void * _Nullable (* _Nonnull)(void * _Nullable),  //该线程运行函数的地址
     void * _Nullable __restrict);  //运行函数所需的参数
     *返回值:
     *若线程创建成功,则返回0
     *若线程创建失败,则返回出错编号
     */

    //
    pthread_t thread = NULL;
    NSString *params = @"Hello World";
    int result = pthread_create(&thread, NULL, threadTask, (__bridge void *)(params));
    result == 0 ? NSLog(@"creat thread success") : NSLog(@"creat thread failure");
    //设置子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源
    pthread_detach(thread);
}

void *threadTask(void *params) {
    NSLog(@"%@ - %@", [NSThread currentThread], (__bridge NSString *)(params));
    return NULL;
}

出口结果:

ThreadDemo[1197:143578] creat thread success
ThreadDemo[1197:143649] <NSThread: 0x600000262e40>{number = 3, name = (null)} - Hello World

由打印结果来拘禁,该任务是在新开辟的线程中施行之,但是觉得用起过不协调,很多事物需要团结管理,单单是天职队列以及线程生命周期的田间管理就是足够你头疼的,那您勾勒起的代码还能是艺术啊!其实用弃这套API很少用,是坐我们发再度好之抉择:NSThread

输出结果:

NSThread

哟呀,它面向对象,再失探访苹果提供的API,对比一下Pthreads,简单明了,人生好像又载了阳光与想,我们事先来同样看一下系提供于咱的API自然就是掌握怎么用了,来来来,我给你注释一下什么:

@interface NSThread : NSObject
//当前线程
@property (class, readonly, strong) NSThread *currentThread;
//使用类方法创建线程执行任务
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//判断当前是否为多线程
+ (BOOL)isMultiThreaded;
//指定线程的线程参数,例如设置当前线程的断言处理器。
@property (readonly, retain) NSMutableDictionary *threadDictionary;
//当前线程暂停到某个时间
+ (void)sleepUntilDate:(NSDate *)date;
//当前线程暂停一段时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//退出当前线程
+ (void)exit;
//当前线程优先级
+ (double)threadPriority;
//设置当前线程优先级
+ (BOOL)setThreadPriority:(double)p;
//指定线程对象优先级 0.0~1.0,默认值为0.5
@property double threadPriority NS_AVAILABLE(10_6, 4_0);
//服务质量
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
//线程名称
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
//栈区大小
@property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0);
//是否为主线程
@property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
//获取主线程
@property (class, readonly, strong) NSThread *mainThread NS_AVAILABLE(10_5, 2_0);
//初始化
- (instancetype)init NS_AVAILABLE(10_5, 2_0) NS_DESIGNATED_INITIALIZER;
//实例方法初始化,需要再调用start方法
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//线程状态,正在执行
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);
//线程状态,正在完成
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);
//线程状态,已经取消
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);
//取消,仅仅改变线程状态,并不能像exist一样真正的终止线程
- (void)cancel NS_AVAILABLE(10_5, 2_0);
//开始
- (void)start NS_AVAILABLE(10_5, 2_0);
//线程需要执行的代码,一般写子类的时候会用到
- (void)main NS_AVAILABLE(10_5, 2_0);
@end

另外,还有一个NSObject的分类,瞅一眼:
@interface NSObject (NSThreadPerformAdditions)
//隐式的创建并启动线程,并在指定的线程(主线程或子线程)上执行方法。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (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 NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
@end

方的介绍您还满意吗?小之增援您下充斥同摆放图片,您瞧好:

-(void)creatBigImageView{
    self.bigImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:_bigImageView];
    UIButton *startButton = [UIButton buttonWithType:UIButtonTypeSystem];
    startButton.frame = CGRectMake(0, 0, self.view.frame.size.width / 2, 50);
    startButton.backgroundColor = [UIColor grayColor];
    [startButton setTitle:@"开始加载" forState:UIControlStateNormal];
    [startButton addTarget:self action:@selector(loadImage) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:startButton];

    UIButton *jamButton = [UIButton buttonWithType:UIButtonTypeSystem];
    jamButton.frame = CGRectMake(self.view.frame.size.width / 2, 0, self.view.frame.size.width / 2, 50);
    jamButton.backgroundColor = [UIColor grayColor];
    [jamButton setTitle:@"阻塞测试" forState:UIControlStateNormal];
    [jamButton addTarget:self action:@selector(jamTest) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:jamButton];
}

-(void)jamTest{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"线程阻塞" message:@"" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
    [alertView show];
}


-(void)loadImage{
    NSURL *imageUrl = [NSURL URLWithString:@"http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg"];
    NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
    [self updateImageData:imageData];
}

-(void)updateImageData:(NSData*)imageData{
    UIImage *image = [UIImage imageWithData:imageData];
    self.bigImageView.image = image;
}

运作结果:

咱得以领略的相,主线程阻塞了,用户不可以进行任何操作,你呈现了这样的应用为?
因此我们这么改一下:

-(void)creatBigImageView{
    self.bigImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:_bigImageView];
    UIButton *startButton = [UIButton buttonWithType:UIButtonTypeSystem];
    startButton.frame = CGRectMake(0, 20, self.view.frame.size.width / 2, 50);
    startButton.backgroundColor = [UIColor grayColor];
    [startButton setTitle:@"开始加载" forState:UIControlStateNormal];
    [startButton addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:startButton];

    UIButton *jamButton = [UIButton buttonWithType:UIButtonTypeSystem];
    jamButton.frame = CGRectMake(self.view.frame.size.width / 2, 20, self.view.frame.size.width / 2, 50);
    jamButton.backgroundColor = [UIColor grayColor];
    [jamButton setTitle:@"阻塞测试" forState:UIControlStateNormal];
    [jamButton addTarget:self action:@selector(jamTest) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:jamButton];
}

-(void)jamTest{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"阻塞测试" message:@"" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
    [alertView show];
}

-(void)loadImageWithMultiThread{
    //方法1:使用对象方法
    //NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
    //⚠️启动一个线程并非就一定立即执行,而是处于就绪状态,当CUP调度时才真正执行
    //[thread start];

    //方法2:使用类方法
    [NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
}

-(void)loadImage{
    NSURL *imageUrl = [NSURL URLWithString:@"http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg"];
    NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
    //必须在主线程更新UI,Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装),waitUntilDone:是否线程任务完成执行
    [self performSelectorOnMainThread:@selector(updateImageData:) withObject:imageData waitUntilDone:YES];

    //[self updateImageData:imageData];
}


-(void)updateImageData:(NSData*)imageData{
    UIImage *image = [UIImage imageWithData:imageData];
    self.bigImageView.image = image;
}

运作结果:

哎呀呀,用几近线程果然能迎刃而解线程阻塞的问题,并且NSThread也比Pthreads好用,仿佛你针对会熟练使用多线程又发生矣一丝丝晨光。假如我发成百上千例外品种的任务,每个任务中还有联系和依靠,你是不是以懵逼了,上面的而是匪是当又白看了,其实开中本身道NSThread从而到绝多的即是[NSThread currentThread];了。(不要慌,往下看…
…)


ThreadDemo[1197:143578] creat thread success
ThreadDemo[1197:143649] {number = 3, name = (null)} - Hello World

GCD

GCD,全名Grand Central Dispatch,中文名郭草地,是根据C语言的平等效仿多线程开发API,一听名字就是是单狠角色,也是时下苹果官方推荐的多线程开发方式。可以说凡是使用方便,又不错过逼格。总体来说,他解决自己关系的方面直接操作线程带来的难题,它自动帮助你管理了线程的生命周期以及任务之行规则。下面我们见面一再的协商一个乐章,那就算是任务,说白了,任务实质上就算是你要执行的那段代码

于打印的结果来拘禁, 该任务是当新开辟的线程中尽之,
但是觉得用起来超过不团结, 很多物要好管理,
单单是任务队列以及线程生命周期的保管虽够头疼的.之所以抛弃这套API很少用,
是因为我们来再次好之精选:NSThread

职责管理章程——队列

地方说当我们设管制几近只任务时,线程开发让咱带了迟早之技术难度,或者说勿方便性,GCD给起了我们归总管理任务之法子,那就算是班。我们来拘禁一下iOS差不多线程操作中的队:(⚠️不管是串行还是并行,队列都是比照FIFO的条件依次触发任务)

NSThread是劈对象的, 所以操作起来会便许多,一起来探它的API吧

些微个通用队列:
  • 差行队列:所有任务会当平长条线程中实行(有或是现阶段线程也发出或是新开辟的线程),并且一个任务尽完毕后,才起实行下一个任务。(等待完成)
  • 彼此队列:可以拉开多长长的线程并行执行任务(但未肯定会开启新的线程),并且当一个职责放到指定线程开始实行时,下一个任务便足以起来推行了。(等待发生)
@interface NSThread : NSObject
//当前线程
@property (class, readonly, strong) NSThread *currentThread;
//使用类方法创建线程执行任务
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//判断当前是否为多线程
+ (BOOL)isMultiThreaded;
//指定线程的线程参数,例如设置当前线程的断言处理器。
@property (readonly, retain) NSMutableDictionary *threadDictionary;
//当前线程暂停到某个时间
+ (void)sleepUntilDate:(NSDate *)date;
//当前线程暂停一段时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//退出当前线程
+ (void)exit;
//当前线程优先级
+ (double)threadPriority;
//设置当前线程优先级
+ (BOOL)setThreadPriority:(double)p;
//指定线程对象优先级 0.0~1.0,默认值为0.5
@property double threadPriority NS_AVAILABLE(10_6, 4_0);
//服务质量
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
//线程名称
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
//栈区大小
@property NSUInteger stackSize NS_AVAILABLE(10_5, 2_0);
//是否为主线程
@property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
//获取主线程
@property (class, readonly, strong) NSThread *mainThread NS_AVAILABLE(10_5, 2_0);
//初始化
- (instancetype)init NS_AVAILABLE(10_5, 2_0) NS_DESIGNATED_INITIALIZER;
//实例方法初始化,需要再调用start方法
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//线程状态,正在执行
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);
//线程状态,正在完成
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);
//线程状态,已经取消
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);
//取消,仅仅改变线程状态,并不能像exist一样真正的终止线程
- (void)cancel NS_AVAILABLE(10_5, 2_0);
//开始
- (void)start NS_AVAILABLE(10_5, 2_0);
//线程需要执行的代码,一般写子类的时候会用到
- (void)main NS_AVAILABLE(10_5, 2_0);
@end
另外,还有一个NSObject的分类,瞅一眼:
@interface NSObject (NSThreadPerformAdditions)
//隐式的创建并启动线程,并在指定的线程(主线程或子线程)上执行方法。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- (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 *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
@end
少数个特殊班:
  • 主队列:系统为我们创建好之一个串行队列,牛逼的处在当被它管理必须以主线程中实行之职责,属于有劳保的。
  • 大局队列:系统吧我们创建好的一个互队列,使用起来与我们温馨创办的彼此队列无精神差别。

连下去我们下充斥同布置图纸,简单以下:

职责履行措施

说罢班,相应的,任务除了管理,还得执行,要不然有钱莫消费,掉了白,并且在GCD中连无克一直开辟线程执行任务,所以当职责在队列之后,GCD给闹了零星种植实施方式——同步施行(sync)和异步执行(async)。

  • 手拉手执行:在时下线程执行任务,不会见开发新的线程。必须顶及Block函数执行了后,dispatch函数才会回去。
  • 异步执行:可以以初的线程中实行任务,但不自然会开发新的线程。dispatch函数会即时回,
    然后Block在后台异步执行。
- (void)createBigImageView {
    self.bigImageView = [[UIImageView alloc] initwithFrame:self.view.bounds];
   [self.view addSubview:self.bigImageView];
   UIButton *startButton = [UIButton buttonWithType:UIButtonTypeSystem];
    startButton.frame = CGRectMake(0, 0, self.view.frame.size.width / 2, 50);
    startButton.backgroundColor = [UIColor grayColor];
    [startButton setTitle:@"开始加载" forState:UIControlStateNormal];
    [startButton addTarget:self action:@selector(loadImage) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:startButton];

    UIButton *jamButton = [UIButton buttonWithType:UIButtonTypeSystem];
    jamButton.frame = CGRectMake(self.view.frame.size.width / 2, 0, self.view.frame.size.width / 2, 50);
    jamButton.backgroundColor = [UIColor grayColor];
    [jamButton setTitle:@"阻塞测试" forState:UIControlStateNormal];
    [jamButton addTarget:self action:@selector(jamTest) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:jamButton];
} 
-(void)jamTest{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@
"线程阻塞"
 message:@
""
 delegate:nil cancelButtonTitle:@
"好"
 otherButtonTitles:nil, nil];
[alertView show];
}

-(void)loadImage{
NSURL *imageUrl = [NSURL URLWithString:@
"[http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg](http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg)"
];
NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
[self updateImageData:imageData];
}

-(void)updateImageData:(NSData*)imageData{
UIImage *image = [UIImage imageWithData:imageData];
self.bigImageView.image = image;
}
上面的这些理论都是本人在群给套路背后总出来的血淋淋的更,与君共享,但是如此写自己猜你必还是无亮,往下看,说不定有喜怒哀乐呢。

把代码粘贴到您的工外,
可以知道看出,主线程阻塞了,用户不得以拓展其他操作.所以我们设修改下:

职责队列组合方式

深信是题目你看了许多浅?是不是圈了也非明了究竟怎么用?这么刚好,我啊是,请相信下面这些自然起你切莫亮并且想要之,我们由区区独极直接的触及切入:

-(void)creatBigImageView{
self.bigImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];  
[self.view addSubview:_bigImageView];
UIButton *startButton = [UIButton buttonWithType:UIButtonTypeSystem];
startButton.frame = CGRectMake(0, 20, self.view.frame.size.width / 2, 50);
startButton.backgroundColor = [UIColor grayColor];
[startButton setTitle:@
"开始加载"
 forState:UIControlStateNormal];
[startButton addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:startButton];
UIButton *jamButton = [UIButton buttonWithType:UIButtonTypeSystem];
jamButton.frame = CGRectMake(self.view.frame.size.width / 2, 20, self.view.frame.size.width / 2, 50);
jamButton.backgroundColor = [UIColor grayColor];
[jamButton setTitle:@
"阻塞测试"
 forState:UIControlStateNormal];
[jamButton addTarget:self action:@selector(jamTest) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:jamButton];
}

-(void)jamTest{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@
"阻塞测试"
 message:@
""
 delegate:nil cancelButtonTitle:@
"好"
 otherButtonTitles:nil, nil];
[alertView show];
}

-(void)loadImageWithMultiThread{
//方法1:使用对象方法
//NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
//??启动一个线程并非就一定立即执行,而是处于就绪状态,当CUP调度时才真正执行
//[thread start];
//方法2:使用类方法
[NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];

}

-(void)loadImage{

NSURL *imageUrl = [NSURL URLWithString:@
"[http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg](http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg)"
];


NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];

//必须在主线程更新UI,Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装),waitUntilDone:是否线程任务完成执行
[self performSelectorOnMainThread:@selector(updateImageData:) withObject:imageData waitUntilDone:YES];
//[self updateImageData:imageData];

}

-(void)updateImageData:(NSData*)imageData{
UIImage *image = [UIImage imageWithData:imageData];
self.bigImageView.image = image;
}

1. 线程死锁

这你是匪是也看了许多蹩脚?哈哈哈!你是未是觉得我还要比方起来复制黏贴了?请向下看:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1========%@",[NSThread currentThread]);
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    NSLog(@"3========%@",[NSThread currentThread]);
}

运转结果:

打印结果:

ThreadDemo[5615:874679] 1========<NSThread: 0x608000072440>{number = 1, name = main}

实在不是自己套路你,我们还是得分析一下为何会死锁,因为必须为那些无备受过套路的民心里留下一截美好的想起,分享代码,我们是认真的!

差不多线程确实解决了线程阻塞问题,并且NSThread比Pthreads好用,
但是要是我们有无数两样门类的任务,
每个任务中还有联系和仰,NSThread又未能够生好之满足我们的要求了,于是GCD出现了.

事务是这般的:

咱们事先做一个概念:- (void)viewDidLoad{} —> 任务A,GCD同步函数
—>任务B。
总而言之乎,大概是这么的,首先,任务A在主队列,并且都初步履行,在主线程打印有1===... ...,然后这时任务B被投入到主队列中,并且一路实施,这尼玛事都很了,系统说,同步执行啊,那自己弗起头新的线程了,任务B说自要是等自其中的Block函数执行就,要无我不怕非归,但是主队列说了,玩蛋去,我是串行的,你得等A执行了才会轮到您,不克十分了规矩,同时,任务B作为任务A的中间函数,必须顶职责B执行完函数回才会履行下一个职责。那即便导致了,任务A等任务B完成才能继续执行,但当串行队列的主队列又未可知给任务B在任务A未形成之前开始实行,所以任务A等正任务B完成,任务B等在任务A完成,等待,永久的等。所以就是死锁了。简单不?下面我们郑重看一下咱们不知不觉书写的代码!

GCD
GCD, 全名是Grand Central Dispatch, 小名叫共产党,是基于C语言的一套多线程开发API, 一听名字就知道非常NB,这也是目前[苹果官方推荐的多线程开发方式.方便使用又有逼格.它解决了我们上面直接操作线程带来的难题,它自动帮我们管理了线程的生命周期以及任务的执行规则.任务,其实就是你要执行的那段代码.
GCD任务管理艺术–队列:简单的保管几近个任务
区区单通用队列

2. 这样非充分锁

未苟就写个最好简单易行的:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1========%@",[NSThread currentThread]);
    NSLog(@"2========%@",[NSThread currentThread]);
    NSLog(@"3========%@",[NSThread currentThread]);
}

打印结果:

ThreadDemo[5803:939324] 1========<NSThread: 0x600000078340>{number = 1, name = main}
ThreadDemo[5803:939324] 2========<NSThread: 0x600000078340>{number = 1, name = main}
ThreadDemo[5803:939324] 3========<NSThread: 0x600000078340>{number = 1, name = main}

事先有人问:顺序打印,没毛病,全以主线程执行,而且顺序执行,那它必然是于主队列同步实施之呦!那为何没死锁?苹果的操作系统果然高深啊!

骨子里这里来一个误区,那就是是任务在主线程顺序执行就是预示队列。其实某些关联都不曾,如果手上当主线程,同步实施任务,不管在啊队任务都是各个执行。把富有任务还因为异步执行之法加盟到主队列中,你见面发觉其啊是各个执行之。

信任你懂者的死锁情况后,你必会手贱改成为这样试试:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1========%@",[NSThread currentThread]);
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    NSLog(@"3========%@",[NSThread currentThread]);
}

打印结果:

ThreadDemo[5830:947858] 1========<NSThread: 0x60000007bb80>{number = 1, name = main}
ThreadDemo[5830:947858] 2========<NSThread: 0x60000007bb80>{number = 1, name = main}
ThreadDemo[5830:947858] 3========<NSThread: 0x60000007bb80>{number = 1, name = main}

乃发觉正常履了,并且是逐一执行的,你是休是只要有所思,没错,你想的和自身怀念的是同的,和上诉情况一样,任务A以主队列中,但是任务B加入到了大局队列,这时候,任务A和天职B没有排的羁绊,所以任务B就优先执行喽,执行完毕之后函数返回,任务A接着执行。

自家猜你一定手贱这么转了:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1========%@",[NSThread currentThread]);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    NSLog(@"3========%@",[NSThread currentThread]);
}

打印结果:

ThreadDemo[5911:962470] 1========<NSThread: 0x600000072700>{number = 1, name = main}
ThreadDemo[5911:962470] 3========<NSThread: 0x600000072700>{number = 1, name = main}
ThreadDemo[5911:962470] 2========<NSThread: 0x600000072700>{number = 1, name = main}

有心人而帅气的乃一定发现未是逐一打印了,而且也非会见死锁,明明还是加到主队列里了哟,其实当任务A以执行时,任务B加入到了主队列,注意哦,是异步执行,所以dispatch函数不见面等到Block执行得才回来,dispatch函数返回后,那任务A可以继续执行,Block任务我们好看在生一样轴顺序进入队列,并且默认无限下同样幅执行。这就算是怎么您看来2===... ...大凡最后输出的了。(⚠️一个函数的产生差不多个里面函数异步执行时,不见面招致死锁的而,任务A执行了后,这些异步执行的里函数会顺序执行)。

串行队列: 所有任务会在一条线程中执行(当前线程或者新开辟的线程), 并且一个任务执行完毕后, 才开始执行下一个任务.(等待完成,好比一个位置的厕所,轮流上).
并行队列: 可以开启多条线程并行执行任务(但不一定会开启新的线程), 并且当一个任务放到指定线程开始执行时, 下一个任务就可以开始执行了.(等待发生,一个厕所多个位置).

咱俩说说队列与履行办法的铺垫

方说了系统自带的少数个队,下面我们来为此好创立的行列研究一下各种搭配情况。
咱先行创造两独序列,并且测试方法都是以主线程遭遇调用:

//串行队列
self.serialQueue = dispatch_queue_create("serialQueue.ys.com", DISPATCH_QUEUE_SERIAL);
//并行队列
self.concurrentQueue = dispatch_queue_create("concurrentQueue.ys.com", DISPATCH_QUEUE_CONCURRENT);

有数单与众不同班

1. 串行队列 + 同步实施
-(void)queue_taskTest{
    dispatch_sync(self.serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_sync(self.serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_sync(self.serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}

打印结果:

ThreadDemo[6735:1064390] 1========<NSThread: 0x600000073cc0>{number = 1, name = main}
ThreadDemo[6735:1064390] 2========<NSThread: 0x600000073cc0>{number = 1, name = main}
ThreadDemo[6735:1064390] 3========<NSThread: 0x600000073cc0>{number = 1, name = main}
ThreadDemo[6735:1064390] 4========<NSThread: 0x600000073cc0>{number = 1, name = main}

全方位还以当前线程顺序执行,也就是说,同步施行不负有开发新线程的力。

主队列: 系统为我们创建好的一个串行队列, 牛逼之处在于它管理必须在主线程中执行的任务, 属于有劳保的.
全局队列: 系统为我们创建好的一个并行队列, 使用起来与我们自己创建的并行队列无本质差别.
2. 串行队列 + 异步执行
-(void)queue_taskTest{
    dispatch_async(self.serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_async(self.serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_async(self.serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}

打印结果:

ThreadDemo[6774:1073235] 4========<NSThread: 0x60800006e9c0>{number = 1, name = main}
ThreadDemo[6774:1073290] 1========<NSThread: 0x608000077000>{number = 3, name = (null)}
ThreadDemo[6774:1073290] 2========<NSThread: 0x608000077000>{number = 3, name = (null)}
ThreadDemo[6774:1073290] 3========<NSThread: 0x608000077000>{number = 3, name = (null)}

事先打印了4,然后逐一以子线程中打印1,2,3。说明异步执行有开拓新线程的能力,并且串行队列必须等交前边一个职责尽完毕才能够开始推行下一个任务,同时,异步执行会要内部函数率先返回,不会见以及正尽的外表函数发生死锁。

任务执行方式

3. 连行队列 + 同步实施
-(void)queue_taskTest{
    dispatch_sync(self.concurrentQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_sync(self.concurrentQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_sync(self.concurrentQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}

运作结果:

ThreadDemo[7012:1113594] 1========<NSThread: 0x60800007e340>{number = 1, name = main}
ThreadDemo[7012:1113594] 2========<NSThread: 0x60800007e340>{number = 1, name = main}
ThreadDemo[7012:1113594] 3========<NSThread: 0x60800007e340>{number = 1, name = main}
ThreadDemo[7012:1113594] 4========<NSThread: 0x60800007e340>{number = 1, name = main}

非开新的线程执行任务,并且Block函数执行到位后dispatch函数才见面回去,才能够延续朝下执行,所以我们看来的结果是逐一打印的。

任务除了管理,还得执行,并且在GCD中并不能直接开辟线程执行任务, 所以在任务加入队列之后, GCD给出了两种执行方式--同步执行(sync)和异步执行(async).
同步执行: 在当前线程执行任务, 不会开辟新的线程.必须等Block函数执行完毕后, dispath函数才会返回.
异步执行: 可以在新的线程中执行任务, 但不一定会开辟新的线程. dispath 函数会立即返回, 然后Block在后台异步执行.
4. 并行队列 + 异步执行
-(void)queue_taskTest{
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}

打印结果:

ThreadDemo[7042:1117492] 1========<NSThread: 0x600000071900>{number = 3, name = (null)}
ThreadDemo[7042:1117491] 3========<NSThread: 0x608000070240>{number = 5, name = (null)}
ThreadDemo[7042:1117451] 4========<NSThread: 0x600000067400>{number = 1, name = main}
ThreadDemo[7042:1117494] 2========<NSThread: 0x600000071880>{number = 4, name = (null)}

开辟了差不多个线程,触发任务的会是逐一的,但是咱看到完成任务之年华可是轻易的,这取决于CPU对于不同线程的调度分配,但是,线程不是无偿无限开拓的,当任务量足够深时,线程是碰头另行用的。

任务队列组合方式
基本上线程最广的问题虽是线程死锁,例如

划一下重点啊

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1========%@",[NSThread currentThread]);
    dispatch_sync(dispatch_get_main_queue(), ^{///这里会崩
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    NSLog(@"3========%@",[NSThread currentThread]);
}
打印结果:ThreadDemo[5615:874679] 1========{number = 1, name = main}
1. 对单核CPU来说,不存真正意义上的竞相,所以,多线程执行任务,其实也仅仅是一个人数在做事,CPU的调度控制了非等待任务之履速率,同时于非等待任务,多线程并从未当真含义提高效率。

为什么会如此也?因为1任务与2职责相互等待,永久的等候,所以便死锁了.
下面具体介绍下汇集整合方式:

2. 线程可以略的看即便是同样段子代码+运行时数。

差行队列 + 同步执行

3. 一并执行会以眼前线程执行任务,不富有开发线程的力量或说并未必要开辟新的线程。并且,同步执行要顶交Block函数执行了,dispatch函数才会回来,从而阻塞同一错行队列中外部方法的履。
-(void)queue_taskTest{
    dispatch_sync(self.serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_sync(self.serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_sync(self.serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}
全部都在当前线程顺序执行,也就是说,同步执行不具备开辟新线程的能力。
4. 异步执行dispatch函数会一直归,Block函数我们得以当其见面当生一致轴加入队列,并因所在队列目前之天职情况最好下一样轴执行,从而不会见死时外部任务的实践。同时,只有异步执行才起开拓新线程的必不可少,但是异步执行不必然会开发新线程。

出错行队列 + 异步执行

5. 如果是行,肯定是FIFO(先进先出),但是哪位先实施完要看第1漫漫。
-(void)queue_taskTest{
    dispatch_async(self.serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_async(self.serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_async(self.serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}
异步执行具有开辟新线程的能力,并且串行队列必须等到前一个任务执行完才能开始执行下一个任务,同时,异步执行会使内部函数率先返回,不会与正在执行的外部函数发生死锁。
6. 比方是串行队列,肯定使等达标一个任务尽好,才能够开始下一个职责。但是彼此队列当及一个职责开始推行后,下一个任务就是足以起来执行。

连行队列 + 同步实施

7. 思念要开辟新线程必须被任务在异步执行,想只要开拓多个线程,只有吃任务在竞相队列中异步执行才足以。执行措施和行类型多叠结于自然水准达到能够实现对代码执行顺序的调度。
-(void)queue_taskTest{
    dispatch_sync(self.concurrentQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_sync(self.concurrentQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_sync(self.concurrentQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}
未开启新的线程执行任务,并且Block函数执行完成后dispatch函数才会返回,才能继续向下执行,所以我们看到的结果是顺序打印的。
8. 联机+串行:未开发新线程,串行执行任务;同步+并行:未开发新线程,串行执行任务;异步+串行:新开拓一漫长线程,串行执行任务;异步+并行:开辟多漫漫新线程,并行执行任务;在主线程遭遇并运用主队列执行任务,会造成死锁。

连行队列 + 异步执行

8. 对此多核CPU来说,线程数量为不能够无限开拓,线程的开辟同样会吃资源,过多线程同时处理任务并无是你想像被之人头大都力十分。
-(void)queue_taskTest{
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:1];
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:2];
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
        //[self nslogCount:10000 number:3];
    });
    NSLog(@"4========%@",[NSThread currentThread]);
}
开辟了多个线程,触发任务的时机是顺序的,但是我们看到完成任务的时间却是随机的,这取决于CPU对于不同线程的调度分配,但是,线程不是无条件无限开辟的,当任务量足够大时,线程是会重复利用的。

GCD其他函数用法

GCD其他函数用法:

1. dispatch_after

拖欠函数用于任务延时执行,其中参数dispatch_time_t表示延时时长,dispatch_queue_t意味着采取谁队。如果队列未主队列,那么任务在主线程执行,如果帮列为全局队列或者好创造的班,那么任务在子线程执行,代码如下:

-(void)GCDDelay{
    //主队列延时
    dispatch_time_t when_main = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
    dispatch_after(when_main, dispatch_get_main_queue(), ^{
        NSLog(@"main_%@",[NSThread currentThread]);
    });
    //全局队列延时
    dispatch_time_t when_global = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));
    dispatch_after(when_global, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"global_%@",[NSThread currentThread]);
    });
    //自定义队列延时
    dispatch_time_t when_custom = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    dispatch_after(when_custom, self.serialQueue, ^{
        NSLog(@"custom_%@",[NSThread currentThread]);
    });
}

打印结果:

ThreadDemo[1508:499647] main_<NSThread: 0x60000007cf40>{number = 1, name = main}
ThreadDemo[1508:499697] global_<NSThread: 0x608000262d80>{number = 3, name = (null)}
ThreadDemo[1508:499697] custom_<NSThread: 0x608000262d80>{number = 3, name = (null)}
1. dispatch_after: 该函数用于任务延时执行,其中参数dispatch_time_t代表延时时长,dispatch_queue_t代表使用哪个队列。如果队列未主队列,那么任务在主线程执行,如果队列为全局队列或者自己创建的队列,那么任务在子线程执行.
2. dispatch: 保证函数在整个生命周期内只会执行一次.
3. dispatch_group_async & dispatch_group_notify: 队列组,当加入到队列组中的所有任务执行完成之后, 会调用dispatch_group_notify函数通知任务全部完成.
4.dispatch_barrier_async: 栅栏函数, 使用此方法创建的任务,会查找当前队列中有没有其他任务要执行,如果有,则等待已有任务执行完毕后再执行,同时,在此任务之后进入队列的任务,需要等待此任务执行完成后,才能执行.
5. dispatch_apply: 该函数用于重复执行某个任务, 如果任务队列是并行队列, 重复执行的任务会并发执行, 如果任务队列为串行队列, 则任务会顺序执行, 该函数为同步函数, 要防止线程阻塞和死锁.
6. dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait
看这几个函数的时候你需要抛开队列,丢掉同步异步,不要把它们想到一起,混为一谈,信号量只是控制任务执行的一个条件而已,相对于上面通过队列以及执行方式来控制线程的开辟和任务的执行,它更贴近对于任务直接的控制。类似于单个队列的最大并发数的控制机制,提高并行效率的同时,也防止太多线程的开辟对CPU早层负面的效率负担
2. dispatch_once

包函数在整整生命周期内单会履行同一糟,看代码。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
}

打印结果:

ThreadDemo[1524:509261] <NSThread: 0x600000262940>{number = 1, name = main}
无论你怎么疯狂的点击,在第一次打印之后,输出台便岿然不动。

NSOperation && NSOperationQueue

3. dispatch_group_async & dispatch_group_notify

试想,现在牛逼的卿要现在简单摆小图,并且你而当少数张图都产充斥完成后将她们并起来,你只要怎么开?我历来不怕不见面将个别摆图并成一张图什么,牛逼的本身怎么可能产生这种想法也?

事实上方法来许多,比如您可以等效摆放同摆放下载,再遵照采用有变量和Blcok实现计数,但是既然今天我们叙到及时,那咱们尽管得可乡随俗,用GCD来落实,有一个神器的东西叫做队列组,当在到队列组中的备任务履行到位之后,会调用dispatch_group_notify函数通知任务尽完了,代码如下:

-(void)GCDGroup{
    //
    [self jointImageView];
    //
    dispatch_group_t group = dispatch_group_create();
    __block UIImage *image_1 = nil;
    __block UIImage *image_2 = nil;
    //在group中添加一个任务
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        image_1 = [self imageWithPath:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502706256731&di=371f5fd17184944d7e2b594142cd7061&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201605%2F14%2F20160514165210_LRCji.jpeg"];

    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        image_2 = [self imageWithPath:@"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=776127947,2002573948&fm=26&gp=0.jpg"];
    });
    //group中所有任务执行完毕,通知该方法执行
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        self.imageView_1.image = image_1;
        self.imageView_2.image = image_2;
        //
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0f);
        [image_2 drawInRect:CGRectMake(0, 0, 100, 100)];
        [image_1 drawInRect:CGRectMake(100, 0, 100, 100)];
        UIImage *image_3 = UIGraphicsGetImageFromCurrentImageContext();
        self.imageView_3.image = image_3;
        UIGraphicsEndImageContext();
    });
}

-(void)jointImageView{
    self.imageView_1 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 50, 100, 100)];
    [self.view addSubview:_imageView_1];

    self.imageView_2 = [[UIImageView alloc] initWithFrame:CGRectMake(140, 50, 100, 100)];
    [self.view addSubview:_imageView_2];

    self.imageView_3 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 200, 200, 100)];
    [self.view addSubview:_imageView_3];

    self.imageView_1.layer.borderColor = self.imageView_2.layer.borderColor = self.imageView_3.layer.borderColor = [UIColor grayColor].CGColor;
    self.imageView_1.layer.borderWidth = self.imageView_2.layer.borderWidth = self.imageView_3.layer.borderWidth = 1;
}
NSOperation 和 NSOperationQueue是苹果对于GCD的封装, NSOperation其实就是我们之前说的任务, 但是这个类不能直接使用, 我们要用他的两个子类, NSBlockOperation和NSInvocationOperation, 而NSOperationQueue呢,其实就是类似于GCD中的队列, 用于管理你加入到其中的任务.
4. dispatch_barrier_async

栅栏函数,这么看来它会挡住或者分隔什么东西,别瞎猜了,反正你以怀疑不针对,看这,使用这方式创建的职责,会寻找当前班中产生没有发生其它任务而实施,如果有,则等待都来任务履行了后还履行,同时,在这任务之后上队列的任务,需要等待这任务履行就后,才能够履行。看代码,老铁。(⚠️
这里并作班必须是和谐创办的。如果选全局队列,这个函数和dispatch_async将见面没有异样。)

-(void)GCDbarrier{

    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务2");
    });

//    dispatch_barrier_async(self.concurrentQueue, ^{
//        NSLog(@"任务barrier");
//    });

//    NSLog(@"big");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务3");
    });
//    NSLog(@"apple");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务4");
    });
}

运作结果:

ThreadDemo[1816:673351] 任务3
ThreadDemo[1816:673353] 任务1
ThreadDemo[1816:673350] 任务2
ThreadDemo[1816:673370] 任务4

凡是休是要您所预期,牛逼大了,下面我们开辟第一句子注释:

-(void)GCDbarrier{

    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务2");
    });

    dispatch_barrier_async(self.concurrentQueue, ^{
        NSLog(@"任务barrier");
    });

//    NSLog(@"big");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务3");
    });
//    NSLog(@"apple");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务4");
    });
}

打印结果:

ThreadDemo[1833:678739] 任务2
ThreadDemo[1833:678740] 任务1
ThreadDemo[1833:678740] 任务barrier
ThreadDemo[1833:678740] 任务3
ThreadDemo[1833:678739] 任务4

本条结果和我们地方的说到契合,我们得以省略的主宰函数执行的各个了,你去大牛又近了一样步,如果现在之若切莫会见存疑还有dispatch_barrier_sync以此函数的说话,说明…
…嘿嘿嘿,我们看一下以此函数和方我们为此到之函数的别,你得想到了,再打开第二个同老三个注释,如下:

-(void)GCDbarrier{

    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务2");
    });

    dispatch_barrier_async(self.concurrentQueue, ^{
        NSLog(@"任务barrier");
    });

    NSLog(@"big");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务3");
    });
    NSLog(@"apple");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务4");
    });
}

运转结果:

ThreadDemo[1853:692434] 任务1
ThreadDemo[1853:692421] 任务2
ThreadDemo[1853:692387] big
ThreadDemo[1853:692421] 任务barrier
ThreadDemo[1853:692387] apple
ThreadDemo[1853:692421] 任务3
ThreadDemo[1853:692434] 任务4

并非心急,我们转移一下函数:

-(void)GCDbarrier{

    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务2");
    });

    dispatch_barrier_sync(self.concurrentQueue, ^{
        NSLog(@"任务barrier");
    });

    NSLog(@"big");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务3");
    });
    NSLog(@"apple");
    dispatch_async(self.concurrentQueue, ^{
        NSLog(@"任务4");
    });
}

打印结果:

ThreadDemo[1874:711841] 任务1
ThreadDemo[1874:711828] 任务2
ThreadDemo[1874:711793] 任务barrier
ThreadDemo[1874:711793] big
ThreadDemo[1874:711793] apple
ThreadDemo[1874:711828] 任务3
ThreadDemo[1874:711841] 任务4

老铁,发现了吗?这简单个函数对于队列的栅栏作用是同样的,但是于该函数相对于其它中间函数遵循了最开头说及的一头跟异步的条条框框。你是免是来硌懵逼,如果你蒙蔽了,那么请于各一个输出后面打印出脚下底线程,如果您要懵逼,那么请您还看,有劳动,不谢!

NSOperation:
它们提供了有关任务的履行, 取消, 以及天天取任务之状态,
添加任务依赖与优先级等方式及性, 相对于GCD提供的法门吧,
更直观,更便民,并且提供了更多之主宰接口.

5. dispatch_apply

该函数用于更执行有任务,如果任务队列是互队列,重复执行的职责会并作执行,如果任务队列为失误行队列,则任务会挨个执行,需要留意的是,该函数为同步函数,要备线程阻塞与死锁哦,老铁。

@interface NSOperation : NSObject {
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif
}
- (void)start;//启动任务 默认加入到当前队列
- (void)main;//自定义NSOperation,写一个子类,重写这个方法,在这个方法里面添加需要执行的操作。
@property (readonly, getter=isCancelled) BOOL cancelled;//是否已经取消,只读
- (void)cancel;//取消任务
@property (readonly, getter=isExecuting) BOOL executing;//正在执行,只读
@property (readonly, getter=isFinished) BOOL finished;//执行结束,只读
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
@property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0);//是否并发,只读
@property (readonly, getter=isReady) BOOL ready;//准备执行
- (void)addDependency:(NSOperation *)op;//添加依赖
- (void)removeDependency:(NSOperation *)op;//移除依赖
@property (readonly, copy) NSArray *dependencies;//所有依赖关系,只读
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};//系统提供的优先级关系枚举
@property NSOperationQueuePriority queuePriority;//执行优先级
@property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);//任务执行完成之后的回调
- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);//阻塞当前线程,等到某个operation执行完毕。
@property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0);//已废弃,用qualityOfService替代。
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);//服务质量,一个高质量的服务就意味着更多的资源得以提供来更快的完成操作。
@property (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);//任务名称
@end
拧行队列:
-(void)GCDApply{
    //重复执行
    dispatch_apply(5, self.serialQueue, ^(size_t i) {
        NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
    });
}

运转结果:

ThreadDemo[1446:158101] 第0次_<NSThread: 0x600000079ac0>{number = 1, name = main}
ThreadDemo[1446:158101] 第1次_<NSThread: 0x600000079ac0>{number = 1, name = main}
ThreadDemo[1446:158101] 第2次_<NSThread: 0x600000079ac0>{number = 1, name = main}
ThreadDemo[1446:158101] 第3次_<NSThread: 0x600000079ac0>{number = 1, name = main}
ThreadDemo[1446:158101] 第4次_<NSThread: 0x600000079ac0>{number = 1, name = main}

NSOperation本身是独抽象类, 不能够一直用, 我们有三种方法予以它新的生

相互队列:
-(void)GCDApply{
    //重复执行
    dispatch_apply(5, self.concurrentQueue, ^(size_t i) {
        NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
    });
}

运转结果:

ThreadDemo[1461:160567] 第2次_<NSThread: 0x608000076000>{number = 4, name = (null)}
ThreadDemo[1461:160534] 第0次_<NSThread: 0x60800006d8c0>{number = 1, name = main}
ThreadDemo[1461:160566] 第3次_<NSThread: 0x60000007d480>{number = 5, name = (null)}
ThreadDemo[1461:160569] 第1次_<NSThread: 0x60000007d440>{number = 3, name = (null)}
ThreadDemo[1461:160567] 第4次_<NSThread: 0x608000076000>{number = 4, name = (null)}
1. NSOperation自定义子类: 我们可以自定义继承与NSOperation的子类, 并重写父类提供的方法, 实现一波具有特殊意义的任务.
2. NSBlockOperation, 系统提供的NSOperation的子类NSBlockOperation
3. NSInvocationOperation, 同样也是系统提供给我们的一个任务类,基于一个target对象以及一个selector来创建任务.
死锁:
-(void)GCDApply{
    //重复执行
    dispatch_apply(5, dispatch_get_main_queue(), ^(size_t i) {
        NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
    });
}

运转结果:

最后来只小结吧
1.于单核CPU来说,不设有真正含义及之彼此,所以多线程执行任务,其实呢止是一个人口于做事,CPU的调度控制了非等待任务的执行速率,
同时对于非等待任务, 多线程并从未真正意义提高效率.

6. dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait

看就几只函数的时段你需要抛开队列,丢掉同步异步,不要拿它们想到一起,混为一谈,信号量只是决定任务执行之一个极而已,相对于点通过队以及实施措施来支配线程的开拓和职责的实践,它重接近对于任务一直的主宰。类似于才个班的不过老并发数的操纵机制,提高并行效率的同时,也防止太多线程的开拓对CPU早层负面的效率负担。
dispatch_semaphore_create创信号量,初始值未可知小于0;
dispatch_semaphore_wait候降低信号量,也不怕是信号量-1;
dispatch_semaphore_signal增强信号量,也即是信号量+1;
dispatch_semaphore_waitdispatch_semaphore_signal平常配对用。
在押一下代码吧,老铁。

-(void)GCDSemaphore{
    //
    //dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_apply(5, self.concurrentQueue, ^(size_t i) {
        //dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(self.concurrentQueue, ^{
            NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
            //dispatch_semaphore_signal(semaphore);
        });
    });
}

公能猜到运行结果为?没错,就是公想的这么,开辟了5单线程执行任务。

ThreadDemo[1970:506692] 第0次_<NSThread: 0x600000070f00>{number = 3, name = (null)}
ThreadDemo[1970:506711] 第1次_<NSThread: 0x6000000711c0>{number = 4, name = (null)}
ThreadDemo[1970:506713] 第2次_<NSThread: 0x6000000713c0>{number = 5, name = (null)}
ThreadDemo[1970:506691] 第3次_<NSThread: 0x600000070f40>{number = 6, name = (null)}
ThreadDemo[1970:506694] 第4次_<NSThread: 0x600000070440>{number = 7, name = (null)}

下同样步而得猜到了,把注释的代码打开:

-(void)GCDSemaphore{
    //
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_apply(5, self.concurrentQueue, ^(size_t i) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(self.concurrentQueue, ^{
            NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
    });
}

运作结果:

ThreadDemo[2020:513651] 第0次_<NSThread: 0x608000073900>{number = 3, name = (null)}
ThreadDemo[2020:513651] 第1次_<NSThread: 0x608000073900>{number = 3, name = (null)}
ThreadDemo[2020:513651] 第2次_<NSThread: 0x608000073900>{number = 3, name = (null)}
ThreadDemo[2020:513651] 第3次_<NSThread: 0x608000073900>{number = 3, name = (null)}
ThreadDemo[2020:513651] 第4次_<NSThread: 0x608000073900>{number = 3, name = (null)}

大明确,我起说之是本着之,哈哈哈哈,信号量是决定任务执行的机要标准,当信号量为0时,所有任务等,信号量越充分,允许而并行执行的天职数更为多。

  1. 线程可以概括的觉得即使是同一段代码+运行时数据.
  2. 并执行会在手上线程执行任务,
    不拥有开发线程的力还是说并未必要开辟新的线程. 并且,
    同步执行要顶及Block函数执行完毕, dispatch函数才会回,
    从而阻塞同一差行队列中外部方法的执行.
  3. 异步执行dispatch函数会一直归,
    Block函数我们好看其重下一致轴加入队列,并冲所在队列目前的职责情况最下一样帧执行,
    从而非见面死时外部任务的执行. 同时,
    只出异步执行才出开拓新线程的必备,但是异步执行不肯定会开发新线程.
  4. 如果是排, 肯定是FIFO(先进先出), 但是何人先实行完要看率先条.
  5. 倘若是出耐性队列, 肯定使等达成一个任务履行就, 才能够开始下一个任务.
    但是彼此队列当及一个职责开始施行后, 下一个任务就是可以开始执行.
  6. 想念使开拓新线程必须叫任务在异步执行, 想只要开辟多单线程,
    只发生被任务在彼此队列中异步执行才足以.
    执行措施及排类型多重合结以大势所趋程度及能够实现对代码执行顺序的调度.
  7. 一同+串行: 未开发新线程, 串行执行任务;
    一起+并行: 未开发新线程, 串行执行任务;
    异步+串行: 新开拓一长达线程, 串行执行任务;
    异步+并行: 开辟对越新线程, 并行执行任务;
    每当主线程遭遇一块运用主队列执行任务, 会造成死锁.
  8. 对多核CPU来说, 线程数量为无可知最好开拓, 线程的开辟同样会耗费资源,
    过多线程同时处理任务并无是我们想象着之总人口大都力大.
GCD就先说到马上,很多API没有涉嫌到,有趣味之同班等好好去探望,重要的凡道和习惯,而休是您看了小。

NSOperation && NSOperationQueue

而点的郭草地倘你学会了,那么就简单只东西而也非必然能够模拟得会!

NSOperation以及NSOperationQueue凡是苹果对此GCD的包,其中为,NSOperation实质上就是是咱地方所说的任务,但是是看似非可知一直使用,我们如果因此他的点滴只子类,NSBlockOperationNSInvocationOperation,而NSOperationQueue否,其实就算是相仿于GCD中之班,用于管理而参加到内部的任务。

NSOperation

她提供了关于任务的实行,取消,以及天天取任务之状态,添加任务依赖和优先级等措施和特性,相对于GCD提供的方式吧,更直观,更方便,并且提供了重新多之决定接口。(很多上,苹果设计之架构是大棒的,不要只是当乎他实现了呀,可能您拟到的东西会又多,一不小心又吹牛逼了,哦呵呵),有几乎独办法及性我们了解一下:

@interface NSOperation : NSObject {
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif
}

- (void)start;//启动任务 默认在当前线程执行
- (void)main;//自定义NSOperation,写一个子类,重写这个方法,在这个方法里面添加需要执行的操作。

@property (readonly, getter=isCancelled) BOOL cancelled;//是否已经取消,只读
- (void)cancel;//取消任务

@property (readonly, getter=isExecuting) BOOL executing;//正在执行,只读
@property (readonly, getter=isFinished) BOOL finished;//执行结束,只读
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
@property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0);//是否并发,只读
@property (readonly, getter=isReady) BOOL ready;//准备执行

- (void)addDependency:(NSOperation *)op;//添加依赖
- (void)removeDependency:(NSOperation *)op;//移除依赖

@property (readonly, copy) NSArray<NSOperation *> *dependencies;//所有依赖关系,只读

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};//系统提供的优先级关系枚举

@property NSOperationQueuePriority queuePriority;//执行优先级

@property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);//任务执行完成之后的回调

- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);//阻塞当前线程,等到某个operation执行完毕。

@property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0);//已废弃,用qualityOfService替代。

@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);//服务质量,一个高质量的服务就意味着更多的资源得以提供来更快的完成操作。

@property (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);//任务名称

@end

然而NSOperation自家是只抽象类,不克一直下,我们有三栽艺术予以其新的人命,就是底下就三单东西,您坐稳看好。

NSOperation自定义子类

立即是本身而说之率先个任务项目,我们得由定义继承给NSOperation的子类,并再写父类提供的法门,实现一波有着特殊意义的职责。比如我们失去下载一个图形:

.h
#import <UIKit/UIKit.h>

@protocol YSImageDownLoadOperationDelegate <NSObject>
-(void)YSImageDownLoadFinished:(UIImage*)image;

@end

@interface YSImageDownLoadOperation : NSOperation

-(id)initOperationWithUrl:(NSURL*)imageUrl delegate:(id<YSImageDownLoadOperationDelegate>)delegate;

@end

.m
#import "YSImageDownLoadOperation.h"

@implementation YSImageDownLoadOperation{
    NSURL *_imageUrl;
    id _delegate;
}

-(id)initOperationWithUrl:(NSURL*)imageUrl delegate:(id<YSImageDownLoadOperationDelegate>)delegate{
    if (self == [super init]) {
        _imageUrl = imageUrl;
        _delegate = delegate;
    }
    return self;
}

-(void)main{
    @autoreleasepool {
        UIImage *image = [self imageWithUrl:_imageUrl];
        if (_delegate && [_delegate respondsToSelector:@selector(YSImageDownLoadFinished:)]) {
            [_delegate YSImageDownLoadFinished:image];
        }
    }
}

-(UIImage*)imageWithUrl:(NSURL*)url{
    NSData *imageData = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:imageData];
    return image;
}


@end

然后调用:
-(void)YSDownLoadImageOperationRun{
    YSImageDownLoadOperation *ysOper = [[YSImageDownLoadOperation alloc] initOperationWithUrl:[NSURL URLWithString:@"http://img5.duitang.com/uploads/item/201206/06/20120606174422_LZSeE.thumb.700_0.jpeg"] delegate:self];
    [ysOper start];
}

-(void)YSImageDownLoadFinished:(UIImage *)image{
    NSLog(@"%@",image);
}

运行打印结果:

ThreadDemo[4141:1100329] <UIImage: 0x60800009f630>, {700, 1050}

啊呵呵,其实自从定义之任务更具备指向性,它可以满足你一定的需求,但是一般用之于少,不知晓凡是盖自己太菜还是确实发生不少更便宜之法子及思路实现这样的逻辑。

NSBlockOperation

老二个,就是系统提供的NSOperation的子类NSBlockOperation,我们看一下外提供的API:

@interface NSBlockOperation : NSOperation {
@private
    id _private2;
    void *_reserved2;
}

+ (instancetype)blockOperationWithBlock:(void (^)(void))block;

- (void)addExecutionBlock:(void (^)(void))block;
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;

@end

异常简单,就即刻几乎单,我们不怕因此它实现一个任务:

-(void)NSBlockOperationRun{
    NSBlockOperation *blockOper = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperationRun_%@_%@",[NSOperationQueue currentQueue],[NSThread currentThread]);
    }];
    [blockOper start];
}

运作结果:

ThreadDemo[4313:1121900] NSBlockOperationRun_<NSOperationQueue: 0x608000037420>{name = 'NSOperationQueue Main Queue'}_<NSThread: 0x60000006dd80>{number = 1, name = main}

我们发现是任务是以时线程顺序执行的,我们发现尚出一个措施addExecutionBlock:试一下:

-(void)NSBlockOperationRun{
    NSBlockOperation *blockOper = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperationRun_1_%@",[NSThread currentThread]);
    }];
    [blockOper addExecutionBlock:^{
        NSLog(@"NSBlockOperationRun_2_%@",[NSThread currentThread]);
    }];
    [blockOper addExecutionBlock:^{
        NSLog(@"NSBlockOperationRun_3_%@",[NSThread currentThread]);
    }];
    [blockOper addExecutionBlock:^{
        NSLog(@"NSBlockOperationRun_4_%@",[NSThread currentThread]);
    }];
    [blockOper start];
}

打印结果:

ThreadDemo[4516:1169835] NSBlockOperationRun_1_<NSThread: 0x60000006d880>{number = 1, name = main}
ThreadDemo[4516:1169875] NSBlockOperationRun_3_<NSThread: 0x600000070800>{number = 4, name = (null)}
ThreadDemo[4516:1169877] NSBlockOperationRun_4_<NSThread: 0x6080000762c0>{number = 5, name = (null)}
ThreadDemo[4516:1169893] NSBlockOperationRun_2_<NSThread: 0x608000076100>{number = 3, name = (null)}

于打印结果来拘禁,这个4单任务是异步并发执行之,开辟了大半长条线程。

NSInvocationOperation

老三单,就是她了,同样也是系统提供被我们的一个任务类,基于一个target对象及一个selector来创造任务,具体代码:

-(void)NSInvocationOperationRun{
    NSInvocationOperation *invocationOper = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperSel) object:nil];
    [invocationOper start];
}
-(void)invocationOperSel{
    NSLog(@"NSInvocationOperationRun_%@",[NSThread currentThread]);
}

运转结果:

ThreadDemo[4538:1173118] NSInvocationOperationRun_<NSThread: 0x60800006e900>{number = 1, name = main}

运行结果和NSBlockOperation单个block函数的执行方同,同步顺序执行。的确系统的包装给予我们关于任务重直观的事物,但是对多单任务的主宰机制并无完善,所以我们出请求下同样员,也许你晤面眼前一亮。

NSOperationQueue

点说道我们创建的NSOperation职责目标可以经过start艺术来施行,同样我们可把这个职责目标上加至一个NSOperationQueue对象被失实施,好想念生好东西,先押一下网的API:

@interface NSOperationQueue : NSObject {
@private
    id _private;
    void *_reserved;
}

- (void)addOperation:(NSOperation *)op;//添加任务
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);//添加一组任务

- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);//添加一个block形式的任务

@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;//队列中所有的任务数组
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);//队列中的任务数

@property NSInteger maxConcurrentOperationCount;//最大并发数

@property (getter=isSuspended) BOOL suspended;//暂停

@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);//名称

@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);//服务质量,一个高质量的服务就意味着更多的资源得以提供来更快的完成操作。

@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);

- (void)cancelAllOperations;//取消队列中的所有任务

- (void)waitUntilAllOperationsAreFinished;//阻塞当前线程,等到队列中的任务全部执行完毕。

#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue NS_AVAILABLE(10_6, 4_0);//获取当前队列
@property (class, readonly, strong) NSOperationQueue *mainQueue NS_AVAILABLE(10_6, 4_0);//获取主队列
#endif

@end

来同样截代码开心开心:

-(void)NSOperationQueueRun{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *invocationOper = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperSel) object:nil];
    [queue addOperation:invocationOper];
    NSBlockOperation *blockOper = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperationRun_%@",[NSThread currentThread]);
    }];
    [queue addOperation:blockOper];
    [queue addOperationWithBlock:^{
        NSLog(@"QUEUEBlockOperationRun_%@",[NSThread currentThread]);
    }];
}

打印结果:

ThreadDemo[4761:1205689] NSBlockOperationRun_<NSThread: 0x600000264480>{number = 4, name = (null)}
ThreadDemo[4761:1205691] NSInvocationOperationRun_<NSThread: 0x600000264380>{number = 3, name = (null)}
ThreadDemo[4761:1205706] QUEUEBlockOperationRun_<NSThread: 0x6000002645c0>{number = 5, name = (null)}

我们发现,加入队列之后不要调用任务之start计,队列会拉扯您管理任务之施行情况。上诉执行结果印证这些任务在队中也出现执行的。

下我们转移一下任务之预先级:
invocationOper.queuePriority = NSOperationQueuePriorityVeryLow;

运作结果:

ThreadDemo[4894:1218440] QUEUEBlockOperationRun_<NSThread: 0x608000268880>{number = 3, name = (null)}
ThreadDemo[4894:1218442] NSBlockOperationRun_<NSThread: 0x60000026d340>{number = 4, name = (null)}
ThreadDemo[4894:1218457] NSInvocationOperationRun_<NSThread: 0x60000026d400>{number = 5, name = (null)}

咱们发现优先级低的天职会后行,但是,这并无是纯属的,还有多事物得以左右CPU分配,以及操作系统对于任务及线程的决定,只能说,优先级会以大势所趋程度及吃优先级赛的职责开始实施。同时,优先级只针对同一队列中的任务使得哦。下面我们虽看一个会面忽视优先级的情况。

添加凭关系
-(void)NSOperationQueueRun{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOper_1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 1000; i++) {
            NSLog(@"blockOper_1_%@_%@",@(i),[NSThread currentThread]);
        }
    }];

    NSBlockOperation *blockOper_2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 1000; i++) {
            NSLog(@"blockOper_2_%@_%@",@(i),[NSThread currentThread]);
        }
    }];

    [blockOper_1 addDependency:blockOper_2];
    [queue addOperation:blockOper_1];
    [queue addOperation:blockOper_2];
}

打印结果:

ThreadDemo[5066:1233824] blockOper_2_0_<NSThread: 0x600000078340>{number = 3, name = (null)}
ThreadDemo[5066:1233824] blockOper_2_1_<NSThread: 0x600000078340>{number = 3, name = (null)}
ThreadDemo[5066:1233824] blockOper_2_2_<NSThread: 0x600000078340>{number = 3, name = (null)}
ThreadDemo[5066:1233824] blockOper_2_3_<NSThread: 0x600000078340>{number = 3, name = (null)}
... ...
ThreadDemo[5066:1233824] blockOper_2_999_<NSThread: 0x600000078340>{number = 3, name = (null)}
ThreadDemo[5066:1233822] blockOper_1_0_<NSThread: 0x60000006ae80>{number = 4, name = (null)}
... ...
ThreadDemo[5066:1233822] blockOper_1_997_<NSThread: 0x60000006ae80>{number = 4, name = (null)}
ThreadDemo[5066:1233822] blockOper_1_998_<NSThread: 0x60000006ae80>{number = 4, name = (null)}
ThreadDemo[5066:1233822] blockOper_1_999_<NSThread: 0x60000006ae80>{number = 4, name = (null)}

经打印结果我们好看看,添加依赖之后,依赖任务要等待给依任务履行完毕之后才会起来执行。⚠️,就算依赖任务的先行级重强,也是给据任务先实施,同时,和事先级不等,依赖关系匪给队列的受制,爱啊哪,只要是自己乘让您,那若得优先实行完毕,我才实施。

班的顶老并发数

就是,这个班最多好来些许任务而履行,或者说最多开发多少条线程,如果设置为1,那就算一律涂鸦只能执行一个任务,但是,不要看马上与GCD的串行队列一样,就算最特别并发数为1,队列任务的实行顺序依然在很多要素。

关于NSOperationQueue再有取消啊,暂停啊等操作方式,大家可试一下,应该注意的是,和学GCD的不二法门不同,不要总是站于面向过程的角度看带这些面向对象的切近,因为其的面相对象化的卷入过程中,肯定有那么些而看不到的面容过程的操作,所以你啊未尝必要就此利用GCD的思想来拟用它们,否则你也许会见头昏的平坍塌糊涂。

线程锁

地方到底将多线程操作的艺术说话了了,下面说一下线程锁机制。多线程操作是基本上个线程并行的,所以同样片资源或于同一时间被多只线程访问,举烂的例子就是是购置火车票,在就剩一个幢时,如果100单线程同时进入,那么可能高达列车时就是有人得干仗了。为了保障世界和平,人民安定,所以我们讲一下这线程锁。我们先行实现平等截代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.sourceArray_m = [NSMutableArray new];
    [_sourceArray_m addObjectsFromArray:@[@"1",@"2",@"3",@"4",@"5",@"6"]];
    [self threadLock];
}
-(void)threadLock{
    for (int i = 0; i < 8; i++) {
        dispatch_async(self.concurrentQueue, ^{
            NSLog(@"%@",[self sourceOut]) ;
        });
    }
}

-(NSString*)sourceOut{
    NSString *source = @"没有了,取光了";
    if (_sourceArray_m.count > 0) {
        source = [_sourceArray_m lastObject];
        [_sourceArray_m removeLastObject];
    }
    return source;
}

运作打印结果:

ThreadDemo[5540:1291666] 6
ThreadDemo[5540:1291669] 6
ThreadDemo[5540:1291682] 5
ThreadDemo[5540:1291667] 4
ThreadDemo[5540:1291683] 3
ThreadDemo[5540:1291666] 2
ThreadDemo[5540:1291669] 1
ThreadDemo[5540:1291682] 没有了,取光了

咱俩发现6受抱出来两次于(因为代码简单,执行效率比较快,所以这种情景不实必现,耐心多尝试几次),这样的话就僵了,一摆票售卖了2软,这么歹之行事是未容许容忍的,所以我们要公平之警卫员——线程锁,我们就算称最直接的有数种植(之前说之GCD的重重道同样可齐于线程锁解决这些题材):

NSLock

代码这样勾画:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.lock = [[NSLock alloc] init];
    self.sourceArray_m = [NSMutableArray new];
    [_sourceArray_m addObjectsFromArray:@[@"1",@"2",@"3",@"4",@"5",@"6"]];
    [self threadLock];
}
-(void)threadLock{
    for (int i = 0; i < 8; i++) {
        dispatch_async(self.concurrentQueue, ^{
            NSLog(@"%@",[self sourceOut]) ;
        });
    }
}
-(NSString*)sourceOut{
    NSString *source = @"没有了,取光了";
    [_lock lock];
    if (_sourceArray_m.count > 0) {
        source = [_sourceArray_m lastObject];
        [_sourceArray_m removeLastObject];
    }
    [_lock unlock];
    return source;
}

运行结果:

ThreadDemo[5593:1298144] 5
ThreadDemo[5593:1298127] 6
ThreadDemo[5593:1298126] 4
ThreadDemo[5593:1298129] 3
ThreadDemo[5593:1298146] 2
ThreadDemo[5593:1298144] 1
ThreadDemo[5593:1298127] 没有了,取光了
ThreadDemo[5593:1298147] 没有了,取光了

然虽保险了被Lock的资源只能以吃一个线程进行走访,从而为便确保了线程安全。

@synchronized

本条也生粗略,有时候为会见为此到这个,要传一个联袂对象(一般就是self),然后以公需要加锁之资源放入代码块被,如果该资源有线程正在访时,会为另外线程等待,直接上代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.sourceArray_m = [NSMutableArray new];
    [_sourceArray_m addObjectsFromArray:@[@"1",@"2",@"3",@"4",@"5",@"6"]];
    [self threadLock];
}
-(void)threadLock{
    for (int i = 0; i < 8; i++) {
        dispatch_async(self.concurrentQueue, ^{
            NSLog(@"%@",[self sourceOut]) ;
        });
    }
}

-(NSString*)sourceOut{
    NSString *source = @"没有了,取光了";
    @synchronized (self) {
        if (_sourceArray_m.count > 0) {
            source = [_sourceArray_m lastObject];
            [_sourceArray_m removeLastObject];
        }
    }
    return source;
}

运转结果:

ThreadDemo[5625:1301834] 5
ThreadDemo[5625:1301835] 6
ThreadDemo[5625:1301837] 4
ThreadDemo[5625:1301852] 3
ThreadDemo[5625:1301834] 1
ThreadDemo[5625:1301854] 2
ThreadDemo[5625:1301835] 没有了,取光了
ThreadDemo[5625:1301855] 没有了,取光了

结语

总的来说该收了!!!就到这吧,小弟曾尽力了,带大家可个门,这长长的路小弟只能陪你运动及及时了。

相关文章