ios多线程浅谈和使用

多线程一直是很操蛋的一个话题,其实如果你理解了很简单,没理解很麻烦;在OC中多线程通常有以下四种

四种线程 pthread_t ,NSThread, GCD,NSOperation+NSOperationQueue

第一种 pthread_t

严格的来说pthread_t并不仅仅属于OC的线程,但是OC牛逼的地方在于它完全兼容C和C++代码,这个玩意个人觉得只需要了解;毕竟这东西的确用的不多,而且相当蛋疼,我们用一个例子说明下这东西的用法 ,既然说了是C语言代码那么就要用C语言的代码搞一搞多线程了,OC需要使用直接复制过去就可以

#include <stdio.h>
#include <pthread.h>

void testThread();
void *start(void *data);

int main(int argc, char const *argv[])
{ 
    puts("================  start =================");
    testThread();
    puts("================  end =================");
    int i;
    for(i=0;i<=10;i++){
        printf("main ===  %d\n", i);
    }
    return 0;
}
void testThread(){
    pthread_t pthread;
    pthread_create(&pthread,NULL,start,NULL);
}
void *start(void *data){
    puts("this is thread test !!");
    int i;
    for(i=0;i<=5;i++){
        printf("start === %d\n", i);
    }
    return NULL;
}

这里写图片描述

OC中需要导入头文件,如上图
输出结果如下图
这里写图片描述

gcc 编译C文件这里就不多解释了,上面的结果说明了一切,至于其他的用法就不多解释了 ,以上就是所谓的 pthread_t的用法了,我们仅作为了解了

第二种 NSThread

NSThread 分为两种:一种是自启动,一种是手动启动;两种线程的子线程体都可以是block或者自定义方法

+ (void)detachNewThreadWithBlock:(void (^)(void))block;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
- (instancetype)initWithBlock:(void (^)(void))block;

自启动:顾名思义就是创建这个线程就会自动执行线程的方法
手动启动:需要手动调用线程的start方法才可以启动
上代码,看看如何使用

-(void)testThreadBlock{
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        for (int i=0; i<=10; i++) {
            NSLog(@"thread  +++++ %d",i);
        }
    }];
    [thread start];

    [NSThread detachNewThreadWithBlock:^{
        for (int i=0; i<=10; i++) {
            NSLog(@"NSThread _____ %d",i);
        }
    }];
}
-(void)testThreadSelector{
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction1) object:nil];
    [thread start];

    [NSThread detachNewThreadSelector:@selector(threadAction2) toTarget:self withObject:nil];

}
-(void)threadAction1{
    for (int i=0; i<=10; i++) {
        NSLog(@"thread  +++++ %d",i);
    }
}
-(void)threadAction2{
    for (int i=0; i<=10; i++) {
        NSLog(@"NSThread _____ %d",i);
    }
}

看下日志输出
这里写图片描述

在NSThread中有一个属性用的比较多 threadPriority 线程优先级属性,这个属性取值是从 0 到 1.0 ;数值越大优先级就越高,通常如果不设置这个值的话系统默认这个值是 0.5
这里写图片描述

如果把这个值越大:有更高的概率先执行,反之则先执行的概率较低我们比较下这是成0.1 和 0.9 比较下debug的日志
这里写图片描述

这里写图片描述

设置成0.1的时候,我们日志的前面大部分是main;当设置成0.9的是,日志前面大部分是thread
类方法和对象方法都可以设置 threadPriority 至于类方法怎么使用其实Apple的API已经讲的很清楚。下面NSThread总结下类方法和对象方法的异同点

方法属性类方法对象方法
创建类方法创建对象方法创建
支持block支持支持
支持SEL支持支持
启动自启动start方法启动
退出exit方法退出cancel方法退出
默认优先级0.50.5
可设置优先级可设置可设置,可以单个线程设置

除此之外NSThread 对 NSObject进行了扩展,我们偶尔能用到的开一个新线程执行 和 放在后台执行;但是要注意的是延时执行是RunLoop相关的,但是我猜想跟线程应该有些关系。。。

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg ;

上面的两个方法其实是OC给我们提供了一种简单的开启新线程的方法,这里就不演示了,方法的名称说明了一切

第三种 GCD

首先GCD是啥子 ? 最好的解释是 基于C语言的一种比较装逼的高级的且适用于OC的多线程 我觉得你能够接触到多线程,想必对GCD会有一点了解 ;回到我们的源头,为什么要用多线程? 其实多线程无非就是用来解决 处理些长时间耗时的或者阻塞当前代码运行的 一种技术吧;比如说大文件下载,列表加载图片等等。。 讲到GCD之前,先引入一个实例
这里写图片描述

在这个页面要下载9张图片,如果使用多线程肯定会滑动卡顿的,可以这么理解:主线程负责UI跟用户的交互,子线程负责下载图片;当主线程去下载图片的时候就会影响用户交互也就是卡顿 —- 题外话 。。。

上面有9张图片在下载, 下载每一张图片的这个过程我们称之为任务 ,也就是上图有9个任务
那么上面的9个任务组成了一个任务组,我们称这个任务组叫队列

任务:耗时操作
队列:多个任务的集合
这里写图片描述

也就是GCD的操作是基于 队列和任务了,那么我们就来探讨下队列;队列是任务的容器,队列分为两种,有些地方也分为三种;串行队列,并行队列,主队列; ,任务也分为两种 : 同步任务 和 异步任务 ;也就是 任务和队列的组合形式 一共有6中 ,我们逐个来看下

  • 串行队列 同步任务
  • 串行队列 异步任务
  • 并行队列 同步任务
  • 并行队列 异步任务
  • 主队列 同步任务
  • 主队列 异步任务
1 串行队列 同步任务

串行队列创建

-(void)queueTest1{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);


    dispatch_sync(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_sync1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    dispatch_sync(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a2.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
           NSLog(@"dispatch_sync2 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));

    NSLog(@"after download  ====== %ld",[NSThread currentThread].hash);
}

队列创建包含两个参数
第一个是队列的标识 “hebiao.queue” 自定义标识
第二个是串行队列的标志 DISPATCH_QUEUE_SERIAL 系统常量

dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_SERIAL);

创建一个串行队列

 dispatch_sync(queue, ^{

    });

创建一个同步任务 , 如何知道是同步任务还是 异步任务了 ? 我们看下日志输出

2017-08-30 09:33:15.677615+0800 TestNSProxy[9889:3274457] currentThread ====== 6174428672
2017-08-30 09:33:15.677692+0800 TestNSProxy[9889:3274457] mainThread  ====== 6174428672
2017-08-30 09:33:23.107414+0800 TestNSProxy[9889:3274457] dispatch_sync1 ======= 6174428672   imagesize = {2592, 1552}
2017-08-30 09:33:23.226778+0800 TestNSProxy[9889:3274457] dispatch_sync2 ======= 6174428672   imagesize = {500, 313}
2017-08-30 09:33:23.226852+0800 TestNSProxy[9889:3274457] after download  ====== 6174428672

我们发现线程的hash值是一样的然后 下载图片算是一个耗时操作并且第一张图片下载完成之后就下载第二张,按照代码的顺序所以我们可以得出结论如下 串行队列 同步任务: 不开启新的线程,按队列的顺序执行

2 串行队列 异步任务

串行队列创建 跟上面是一样的 。。


-(void)queueTest2{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);

    dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_async1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    dispatch_async(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a2.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_async2 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    NSLog(@"after download  ====== %ld",[NSThread currentThread].hash);
}

队列创建包含两个参数
第一个是队列的标识 “hebiao.queue” 自定义标识
第二个是串行队列的标志 DISPATCH_QUEUE_SERIAL 系统常量

dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_SERIAL);

创建一个串行队列 , 这个跟上面是一样的 ,不同的是异步任务

 dispatch_async(queue, ^{

    });

创建一个异步任务; 我们发现 这两个很像 同步任务dispatch_sync 和异步任务dispatch_async 就差一个字母 a 看下日志

2017-08-30 09:44:31.416907+0800 TestNSProxy[9893:3277114] currentThread ====== 6241615616
2017-08-30 09:44:31.416957+0800 TestNSProxy[9893:3277114] mainThread  ====== 6241615616
2017-08-30 09:44:31.417000+0800 TestNSProxy[9893:3277114] after download  ====== 6241615616
2017-08-30 09:44:42.403647+0800 TestNSProxy[9893:3277155] dispatch_async1 ======= 6243653760   imagesize = {2592, 1552}
2017-08-30 09:44:42.530342+0800 TestNSProxy[9893:3277155] dispatch_async2 ======= 6243653760   imagesize = {500, 313}

我们异步任务中线程的hash值和主线程不一样,然后 下载图片算是一个耗时操作并且第一张图片下载完成之后就下载第二张,按照代码的顺序执行,所以我们可以得出结论如下 串行队列 异步步任务: 为所有任务开启一个新的线程,按队列的顺序执行

3 并行队列 同步任务

串行队列和并行队列关键字修饰不一样 ,直接上代码

-(void)queueTest3{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);

    dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_sync1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    dispatch_sync(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_sync2 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    NSLog(@"after download  ====== %ld",[NSThread currentThread].hash);

}

队列创建包含两个参数
第一个是队列的标识 “hebiao.queue” 自定义标识
第二个是并行队列的标志 DISPATCH_QUEUE_CONCURRENT 系统常量

dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_CONCURRENT);

创建一个串行队列

 dispatch_sync(queue, ^{

    });

创建一个同步任务 我们看下日志输出

2017-08-30 09:55:27.481648+0800 TestNSProxy[9905:3280774] currentThread ====== 6174477056
2017-08-30 09:55:27.481706+0800 TestNSProxy[9905:3280774] mainThread  ====== 6174477056
2017-08-30 09:55:35.257421+0800 TestNSProxy[9905:3280774] dispatch_sync1 ======= 6174477056   imagesize = {2592, 1552}
2017-08-30 09:55:35.295198+0800 TestNSProxy[9905:3280774] dispatch_sync2 ======= 6174477056   imagesize = {146, 92}
2017-08-30 09:55:35.295324+0800 TestNSProxy[9905:3280774] after download  ====== 6174477056

我们发现只要是同步任务,不管是并行队列和串行队列 输出是一样的 ,不开启新线程并且顺序执行 我们可以得出结论如下 并行队列 同步任务: 不开启新的线程,按队列的顺序执行

4 并行队列 异步任务

直接上代码 。。

-(void)queueTest4{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);

    dispatch_queue_t queue = dispatch_queue_create("hebiao.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_async1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    dispatch_async(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_async2 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });
    NSLog(@"after download  ====== %ld",[NSThread currentThread].hash);

}

我们直接输出日志,在输出日志之前先申明下 https://hebiao.online/img/a1.jpg 这个图片较大, 而https://hebiao.online/img/tomcat.png这个图片很小 ,我们看下日志

2017-08-30 10:05:52.386516+0800 TestNSProxy[9909:3283332] currentThread ====== 6174457024
2017-08-30 10:05:52.386569+0800 TestNSProxy[9909:3283332] mainThread  ====== 6174457024
2017-08-30 10:05:52.386616+0800 TestNSProxy[9909:3283332] after download  ====== 6174457024
2017-08-30 10:05:52.845416+0800 TestNSProxy[9909:3283372] dispatch_async2 ======= 6174523776   imagesize = {146, 92}
2017-08-30 10:05:59.969279+0800 TestNSProxy[9909:3283369] dispatch_async1 ======= 6241632640   imagesize = {2592, 1552}

我们发现 ,有三个不同的线程hash值;也就是除了主线程之外分别为两个任务开启了两个子线程,并且不是按照顺序执行的,而且我们发现图片小的先下载完成,总结下并行队列 异步步任务: 为每个任务开启新的线程,不按队列的顺序执行

5 主队列 同步任务

上代码

-(void)queueTest5{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);

    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_sync1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    dispatch_sync(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_sync2 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    NSLog(@"after download  ====== %ld",[NSThread currentThread].hash);

}

创建主队列

 dispatch_queue_t queue = dispatch_get_main_queue();

其他的地方是一样的,但是执行的时候就报错了 。。。。。
总结下主队列 同步任务: 直接报错

6 主队列 异步任务

上代码

-(void)queueTest6{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);

    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_async1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    dispatch_async(queue, ^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"dispatch_async2 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    });

    NSLog(@"after download  ====== %ld",[NSThread currentThread].hash);

}

跟上面类似,只是同步和异步的区别 ,然后看下日志的输出

2017-08-30 10:29:50.184149+0800 TestNSProxy[9936:3290719] currentThread ====== 6174437632
2017-08-30 10:29:50.184198+0800 TestNSProxy[9936:3290719] mainThread  ====== 6174437632
2017-08-30 10:29:50.184225+0800 TestNSProxy[9936:3290719] after download  ====== 6174437632
2017-08-30 10:30:00.720892+0800 TestNSProxy[9936:3290719] dispatch_async1 ======= 6174437632   imagesize = {2592, 1552}
2017-08-30 10:30:00.759890+0800 TestNSProxy[9936:3290719] dispatch_async2 ======= 6174437632   imagesize = {146, 92}

看日志我们发现,线程hash值是一样的,但是执行的顺序不一样 总结下主队列 异步任务: 不开启新的线程,不按队列的顺序执行
总结下以上几种队列

队列同步任务异步任务
串行队列不开新线程,顺序执行为任务开启一个新线程,顺序执行
并行队列不开新线程,顺序执行为每个任务开启一个新线程,不按顺序执行
主队列无效报错不开新线程 ,不按顺序执行

结论

同步任务:不开启新线程,顺序执行
异步任务:主队列不开新线程

第四种 NSOperation+NSOperationQueue

NSOperation 按照GCD的理解就是任务
NSOperationQueue 同样按照GCD的理解就是队列
NSOperation 有两个子类 NSBlockOperation 和 NSInvocationOperation 通过名称不难看出,一个是block方法,一个是SEL方法;我们一般使用的是NSOperation的子类或者自定义一个类继承NSOperation 跟NSThread是类似的 ,分别来看下用法

1 NSBlockOperation
-(void)queueTest7{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);
    // NSBlockOperation *operatiion = [[NSBlockOperation alloc] init]; 两种都一样
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation1  ====== %ld",[NSThread currentThread].hash);

    }];
    [operation addExecutionBlock:^{
         NSLog(@"operation2  ====== %ld",[NSThread currentThread].hash);
    }];

    [operation start];

}

2017-08-30 11:28:55.177034+0800 TestNSProxy[9955:3301574] currentThread ====== 6241573952
2017-08-30 11:28:55.177090+0800 TestNSProxy[9955:3301574] mainThread  ====== 6241573952
2017-08-30 11:28:55.177263+0800 TestNSProxy[9955:3301617] operation1  ====== 6174533376
2017-08-30 11:28:55.177277+0800 TestNSProxy[9955:3301617] operation2  ====== 6174533376

单纯的 NSBlockOperation 会开启一个新线程并且按顺序执行; 跟我们上面提到的 串行队列 异步任务 很像

2 NSInvocationOperation

先上代码

-(void)queueTest8{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(action:) object:NULL];
    [operation start];
}
-(void)action:(id)sender{
     NSLog(@"operation  ====== %ld",[NSThread currentThread].hash);
}

2017-08-30 11:48:05.237857+0800 TestNSProxy[9965:3306128] currentThread ====== 6174532032
2017-08-30 11:48:05.237909+0800 TestNSProxy[9965:3306128] mainThread  ====== 6174532032
2017-08-30 11:48:05.238099+0800 TestNSProxy[9965:3306128] operation  ====== 6174532032

这个类很尴尬 , 只有一个所谓的任务,而且没有开启新的线程

3 NSOperationQueue

会有这么一个问题,如果我想搞一个 并行队列 异步任务 了 ? 按照上面的方式貌似没有什么搞头 ,那么 。。。 就要引入 NSOperationQueue ,先来看下NSOperationQueue的初始化

-(void)queueTest9{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
         NSLog(@"queue  ====== %ld",[NSThread currentThread].hash);
    }];

    NSOperationQueue *mqueue = [NSOperationQueue mainQueue];
    [mqueue addOperationWithBlock:^{
         NSLog(@"mqueue  ====== %ld",[NSThread currentThread].hash);
    }];
}

2017-08-30 14:10:29.330361+0800 TestNSProxy[10105:3345221] currentThread ====== 6174459328
2017-08-30 14:10:29.330427+0800 TestNSProxy[10105:3345221] mainThread  ====== 6174459328
2017-08-30 14:10:29.331572+0800 TestNSProxy[10105:3345270] queue  ====== 6241580672
2017-08-30 14:10:29.336198+0800 TestNSProxy[10105:3345221] mqueue  ====== 6174459328

NSOperationQueue 初始化有方法有两种,一种是 alloc 一种是 mainQueue ;我们发现 alloc创建的queue是开启了一个新的线程,而mainQueue 并没有开启新线程 ;还有 一点比较重要的是 NSOperationQueue 无需手动启动
我们可以把NSOperationQueue 跟前面的 NSOperation 关联起来

-(void)queueTest10{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);
    NSBlockOperation *blockOperation1 = [[NSBlockOperation alloc] init];
    [blockOperation1 addExecutionBlock:^{
                NSLog(@"blockOperation1 ======= %ld   ",[NSThread currentThread].hash);
    }];

    NSInvocationOperation *blockOperation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(opAction:) object:nil];

    NSLog(@"  === over === ");

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:blockOperation1];
    [queue addOperation:blockOperation2];
}
-(void)opAction:(id)sender{
      NSLog(@"blockOperation2 ======= %ld  ",[NSThread currentThread].hash );
}

2017-08-30 14:33:28.151707+0800 TestNSProxy[10122:3352107] currentThread ====== 6174484288
2017-08-30 14:33:28.151762+0800 TestNSProxy[10122:3352107] mainThread  ====== 6174484288
2017-08-30 14:33:28.152059+0800 TestNSProxy[10122:3352107]   === over ===
2017-08-30 14:33:28.152201+0800 TestNSProxy[10122:3352159] blockOperation1 ======= 6176507712
2017-08-30 14:33:28.152299+0800 TestNSProxy[10122:3352159] blockOperation2 ======= 6176507712

通过上面的代码我们把NSOperationQueue 和 NSOperation 联系在一起了,但是这貌似没有跟GCD相比少点什么 。。。 如何让多个任务同时执行了 ?

-(void)queueTest11{
    NSLog(@"currentThread ====== %ld",[NSThread currentThread].hash);
    NSLog(@"mainThread  ====== %ld",[NSThread mainThread].hash);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    [queue addOperationWithBlock:^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/a1.jpg"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"queue1 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"queue2 ====== %ld",[NSThread currentThread].hash);
    }];
    [queue addOperationWithBlock:^{
        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://hebiao.online/img/tomcat.png"]];
        UIImage *img = [[UIImage alloc] initWithData:data];
        NSLog(@"queue3 ======= %ld   imagesize = %@",[NSThread currentThread].hash ,NSStringFromCGSize(img.size));
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"queue4 ====== %ld",[NSThread currentThread].hash);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"queue5 ====== %ld",[NSThread currentThread].hash);
    }];
    NSLog(@"  === over === ");
}

2017-08-30 14:45:24.603104+0800 TestNSProxy[10134:3356916] currentThread ====== 6241543232
2017-08-30 14:45:24.603159+0800 TestNSProxy[10134:3356916] mainThread  ====== 6241543232
2017-08-30 14:45:24.603443+0800 TestNSProxy[10134:3356916]   === over ===
2017-08-30 14:45:32.057217+0800 TestNSProxy[10134:3356962] queue1 ======= 6174480896   imagesize = {2592, 1552}
2017-08-30 14:45:32.058261+0800 TestNSProxy[10134:3356963] queue2 ====== 6174466304
2017-08-30 14:45:32.150285+0800 TestNSProxy[10134:3356963] queue3 ======= 6174466304   imagesize = {146, 92}
2017-08-30 14:45:32.150746+0800 TestNSProxy[10134:3356963] queue4 ====== 6174466304
2017-08-30 14:45:32.150973+0800 TestNSProxy[10134:3356963] queue5 ====== 6174466304

NSOperationQueue 提供了 一个 maxConcurrentOperationCount 最大线程数量;我们可以这么理解,除了主线程之外,最多可以开启多少个任务数量,有点像迅雷下载 。。。。 上面的任务数设置为1,但是看到上面的日志 ,我觉得这个说法还得加个修饰 同时开启的最大线程数量
当我把上面的数值调整的时候

queue.maxConcurrentOperationCount = 5;

2017-08-30 14:48:43.145571+0800 TestNSProxy[10138:3358375] currentThread ====== 6176514752
2017-08-30 14:48:43.145625+0800 TestNSProxy[10138:3358375] mainThread  ====== 6176514752
2017-08-30 14:48:43.145863+0800 TestNSProxy[10138:3358375]   === over ===
2017-08-30 14:48:43.146940+0800 TestNSProxy[10138:3358423] queue2 ====== 6176556160
2017-08-30 14:48:43.150508+0800 TestNSProxy[10138:3358422] queue4 ====== 6176549760
2017-08-30 14:48:43.150566+0800 TestNSProxy[10138:3358422] queue5 ====== 6176549760
2017-08-30 14:48:44.280059+0800 TestNSProxy[10138:3358423] queue3 ======= 6176556160   imagesize = {146, 92}
2017-08-30 14:48:50.769027+0800 TestNSProxy[10138:3358424] queue1 ======= 6176593088   imagesize = {2592, 1552}

我们发现得到的输出顺序变了 。。。。 我们甚至可以得出这样的一个结论
queue.maxConcurrentOperationCount = 1 的时候 NSOperationQueue 为串行队列
而当maxConcurrentOperationCount > 1 的时候 NSOperationQueue 并行队列 ;
并且 maxConcurrentOperationCount 默认为 1

另外NSOperation 还有一个比较常用的功能就是 NSOperation 是可以相互依赖的,有点像Jar包依赖;举个简单的例子,我要吃饭,但是我必须用碗把饭先盛着再吃,把饭盛到碗了这个动作可以理解为依赖
没有依赖之前

-(void)queueTest12{

     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation1  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation2  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation3  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation4  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation5  ====== %ld",[NSThread currentThread].hash);

    }];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
    [queue addOperation:operation4];
    [queue addOperation:operation5];   
}

2017-08-30 15:05:58.261291+0800 TestNSProxy[10151:3363635] operation2  ====== 6176545728
2017-08-30 15:05:58.261399+0800 TestNSProxy[10151:3363631] operation1  ====== 6243625856
2017-08-30 15:05:58.261440+0800 TestNSProxy[10151:3363635] operation3  ====== 6176545728
2017-08-30 15:05:58.261504+0800 TestNSProxy[10151:3363631] operation4  ====== 6243625856
2017-08-30 15:05:58.261524+0800 TestNSProxy[10151:3363635] operation5  ====== 6176545728

1-5是顺序执行的 , 一旦加入了依赖 就变化 了 。。


-(void)queueTest12{

     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation1  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation2  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation3  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation4  ====== %ld",[NSThread currentThread].hash);

    }];
    NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operation5  ====== %ld",[NSThread currentThread].hash);

    }];
    [operation1 addDependency:operation2];
    [operation2 addDependency:operation3];
    [operation3 addDependency:operation4];
    [operation4 addDependency:operation5];

    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
    [queue addOperation:operation4];
    [queue addOperation:operation5];
}

2017-08-30 15:07:55.438561+0800 TestNSProxy[10153:3364304] operation5  ====== 6241643648
2017-08-30 15:07:55.438650+0800 TestNSProxy[10153:3364304] operation4  ====== 6241643648
2017-08-30 15:07:55.438685+0800 TestNSProxy[10153:3364304] operation3  ====== 6241643648
2017-08-30 15:07:55.438714+0800 TestNSProxy[10153:3364304] operation2  ====== 6241643648
2017-08-30 15:07:55.438743+0800 TestNSProxy[10153:3364304] operation1  ====== 6241643648

顺序变了 , 优先执行 被依赖的 NSBlockOperation。。。。。。
兵无常势 水无常形 具体的项目具体操作 。。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值