博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多线程与网络线程的几种创建方法
阅读量:6692 次
发布时间:2019-06-25

本文共 8266 字,大约阅读时间需要 27 分钟。

 

线程的创建方法

pthread

  • 创建 pthread_create
  • 只要create一次就会创建一个新的线程
  • 系统会自动在子线程中调用传入的函数

    {    // 将耗时的操作放在子线程中    /*    第一个参数:pthread_t *restrict 线程的代号    第二个参数:const pthread_attr_t *restrict 线程的属性    第三个参数:void *(*)(void *) 指向函数的指针,将来线程需要执行的方法    第四个参数:void *restrict 给第三个参数的指向函数 传递的参数    */    pthread_t threadID;    // 只要create一次就会创建一个新的线程    pthread_create(&threadID, NULL, &demo, @"xiao");}void *demo(void * index) {    for (int i = 0; i < 100; ++i) {        NSLog(@"%i------%@", i, [NSThread currentThread]);    }    return NULL; // 比较特殊,要有返回值}

NSThread

注意点:死了不能再活过来(即要重新调用)

  • 1.几种创建方式

    • 1)alloc-initWithTarget方法

      • 注意:需要手动调用start方法启动线程
      • 特点:系统内部会retain当前线程
      • 生命周期:只有线程中的方法执行完毕,系统才会将其释放
      • 代码:

        // 1.创建一个新的线程     NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];    // 2.启动线程    [thread start];
    • 2)detachNewThreadSelector:方法

      • 注意:不需要手动调用start方法启动线程
      • 缺点:没有返回值,不能对线程进行更多的设置
      • 应用场景:需要快速简便的执行线程
      • 代码:

        // 分离出一个新的子线程[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
    • 3)performSelectorInBackground:方法

      • 注意:不需要手动调用start方法启动线程,当前控制器调用(self)
      • 缺点:没有返回值,不能对线程进行更多的设置
      • 应用场景:需要快速简便的执行线程
      • 代码:

        // 用后台线程调用run:函数[self performSelectorInBackground:@selector(run:) withObject:nil];
    • 4)相关属性

  • 2.线程状态

    • 新建状态(New):创建出来
    • 就绪状态(Runnable):调用start
    • 运行状态(Running):被CUP调用
    • 阻塞状态(Blocked):调用了sleep方法/等待同步锁

      // 指定时间(阻塞线程,阻塞5秒的时间)[NSThread sleepForTimeInterval:5.0f];// 指定日期(阻塞线程,阻塞到现在开始的2秒钟之后)[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];// 指定日期(睡眠醒不来)[NSThread sleepUntilDate:[NSDate distantFuture]];[NSThread exit];
    • 死亡状态(Dead):线程任务执行完毕/异常/强制退出(exit)

  • 3.互斥锁(安全隐患)

    • 应用场景:多线程存在资源抢夺(多个线程同时访问某个文件/变量等)
    • 注意点:
      • 只要枷锁就会消耗性能
      • 如果想真正的锁住代码, 那么多个线程必须使用同一把锁才行
      • 加锁的时候尽量缩小范围, 因为范围越大性能就越低
    • 技巧:如何快速记住加锁的单词

      • [NSUserDefaults standardUserDefaults] synchronize快速记忆的方法

        // self是锁对象:唯一性@synchronized:(self) {    // 加锁的内容}
    • 专业术语:线程同步(多条线程在同一条线上执行)

  • 4.原子和非原子

    • atomic:原子属性,为setter方法加锁,线程安全,性能低
    • nonatomic:非原子属性,不会为setter方法加锁,线程不安全,性能高
  • 5.线程间通信

    • 体现:
      • 1.一个线程传递数据给另一个线程
      • 2.在一个线程中执行完特定任务后,让另一个线程执行接下来的任务
  • 附:下载图片

    • 1.获得下载图片的url

      NSURL *url = [NSURL URLWIthString:@"图片下载地址"];

    • 2.下载图片的二进制数据到本地(花费时间最长)

      NSData *imageData = [NSData dataWithContentsOfURL:url];

    • 3.把二进制数据转换成image

      UIImage *image = [UIImage imageWithData:imageData];

    • 4.回到主线程刷新UI(设置图片)很多个方法

      [self performSelector:@selector(showImage:) withObject...];

    • 5.在showImage中设置图片 self.imageView.image = image;

GCD

  • 1.简介:(Grand Central Dispatch)牛逼的中枢调度器
  • 2.特点:
    • 为多核的并行运算提出的方案
    • 自动利用更多的cup内核
    • 自动管理线程的生命周期
    • 缺少
  • 3.任务和队列:执行什么操作,用来存放任务
  • 4.同步和异步(能不能开线程)---封装任务,添加任务到队列中

    • 同步: 只能在当前线程中执行任务,不具备开启新线程的能力,要求立刻执行
    • 异步: 可以在新的线程中执行任务,具备开启新线程的能力
    • 对应的两个函数

      同步函数:dispatch_sync异步函数:dispatch_async
  • 5.队列类型(任务的执行方式)---保持任务,安排|调度任务

    • 并发队列: 允许多个任务并发(同时)执行
    • 串行队列: 一个任务执行完毕后,再执行另一个任务
  • 6.GCD的基本使用

    • 01 异步函数+并发队列:开启多条线程,并发执行任务
    • 02 异步函数+串行队列:开启一条线程,串行执行任务
    • 03 同步函数+并发队列:不开线程,串行执行任务
    • 04 同步函数+串行队列:不开线程,串行执行任务
    • 05 异步函数+主队列:不开线程,在主线程中串行执行任务
    • 06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
      • 主队列特点:如果发现主线程正在执行代码,那么就暂停调度队列里面的任务,即是死锁现象,需要设置一个子控制器来执行
    • 07 注意同步函数和异步函数在执行顺序上面的差异

      • 异步函数:不需要当前代码执行完毕,就可以执行后面的代码
      • 同步函数:要等到当前代码执行完毕,才可以继续往下执行(一直等待)

  • 7.GCD的常用通信代码模板

    • 1.创建队列: 保存任务,安排|调度任务

      // 1.创建队列: 保持任务,安排|调度任务/*第一个参数:const char *label 字符串第二个参数:dispatch_queue_attr_t attr 指示出是并发还是串行DISPATCH_QUEUE_CONCURRENT 并发DISPATCH_QUEUE_SERIAL 串行*/dispatch_queue_t queue = dispatch_queue_create("xiao", DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queue = dispatch_queue_create("xiao", DISPATCH_QUEUE_SERIALS);/*第一个参数:long identifier 队列的优先级, DISPATCH_QUEUE_PRIORITY_DEFAULT == 0第二个参数:unsigned long flags 此参数暂时无用,设置为0*/dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    • 2.创建同步/异步函数: 封装任务,添加任务到队列中

      // 2.异步函数: 封装任务,添加任务到队列中dispatch_async(queue, ^{// 执行代码});// 2.同步函数: 封装任务,添加任务到队列中dispatch_sync(queue, ^{// 执行代码});
    • 3.下载图片小事例

    // 1.创建队列(全局并发队列)dispatch_queue_t queue = dispatch_get_global_queue(0, 0);// 2.下载图片(耗时)-->放在子线程中(异步函数)dispatch_async(queue, ^{    // 创建url    NSURL *url = [NSURL URLWithString:@"http://www.qqjia.com/z/06/tu8000_5.jpg"];    // 通过url将图片转换成二进制NSData    NSData *data = [NSData dataWithContentsOfURL:url];    // 将NSData转换成图片    UIImage *image = [UIImage imageWithData:data];    // 更新UI --> 在主线程中    // 如果是通过异步函数调用, 那么会先执行完所有的代码, 再更新UI    // 如果是同步函数调用, 那么会先更新UI, 再执行其它代码    dispatch_async(dispatch_get_main_queue(), ^{        NSLog(@"%@", [NSThread currentThread].name);        self.imageView.image = image;        NSLog(@"先更新UI");    });    NSLog(@"先执行");});
  • 8.常用函数

    • 1.GCD的延迟执行

      // DISPATCH_TIME_NOW:从什么时候开始计时(现在)// 2.0位置:间隔时间(延迟时间)// dispatch_get_main_queue():队列,决定block在哪个线程中调用,当是主队列时就是主线程调用dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{    // 代码});
    • 2.GCD的栅栏函数(barrier)

      // 栅栏函数:可以控制队列中任务的执行顺序,前面的任务执行完毕后执行后面的任务// 注意:这边只能使用并发队列(但是不能用全局并发队列)dispatch_barrier_async(queue, ^{    NSLog(@"---------");};
    • 3.一次性代码函数

      // 保证整个程序运行中过程中执行一次,不能放在懒加载中,会为空static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{    NSLog(@"----once----");};
    • 4.快速迭代函数

      // 第一个参数:size_t iterations 遍历的次数// 第二个参数:dispatch_queue_t queue 队列,决定block在哪个线程调用,并发队列// 第三个参数:^(size_t) (需要在size_t后面加上一个参数名)索引dispatch_apply(size_t iterations, dispatch_queue_t queue, ^(size_t) {// 代码});
    • 5.队列组

      // 创建队列组dispatch_group_t group = dispatch_group_create();// 创建队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 使用队列组的异步函数,添加数组,监听队列里面任务的执行情况dispatch_group_async(group, queue, ^{    // 代码}// 第一种:(拦截通知)当所有任务都执行完毕后,来到该方法dispatch_group_notify(group, queue, ^{    // 代码}// 第二种:(拦截通知)一直等待,等待所有的任务都执行完毕后继续往下执行 | 阻塞dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
      • 小案例:下载两张图片,合成图片

        代码
  • 9.使用create函数创建的并发队列和全局并发队列的区别

    • 1.全局并发队列在整个应用程序中本身是默认存在的,并且对应有高优先级、默认优先级、低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用。而Crearte函数是实打实的从头开始去创建一个队列。
    • 2.在iOS6.0之前,在GCD中凡是使用了带Crearte和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。当然了,在iOS6.0之后GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就OK。
    • 3.在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)

NSOperation

  • 1.概念:
    • NSOperation是对GCD的包装
    • 两个核心概念[队列+操作]
  • 2.NSOperation基本使用:

    • NSOperation本身是抽象类,只能使用它的子类
    • 三个子类:NSBlockOperation/NSInvocationOperation/自定义继承NSOperation的类
    • NSOperation和NSOperationQueue配合使用实现多线程编程
    • 相关代码:

      • 1) NSBlockOperation

        //1.封装操作NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{// 主线程执行    NSLog(@"download1---%@",[NSThread currentThread]);}];// 追加任务// 追加的任务在子线程中并发执行[op1 addExecutionBlock:^{    NSLog(@"download4---%@",[NSThread currentThread]);}];//2.开始执行[op1 start];
      • 2) NSInvocationOpeartion

        //1.封装操作/*第一个参数:目标对象 self第二个参数:调用方法第三个参数:调用方法需要传递的参数*/NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];//2.启动操作[op1 start];
      • 3) 自定义继承NSOperation的类

        // 自定义一个继承NSOperation的类(重写main函数)- (void)main{    NSLog(@"1---%@", [NSThread currentThread]);}// 封装操作LJSubOperation *subOperation1 = [[LJSubOperation alloc] init];LJSubOperation *subOperation2 = [[LJSubOperation alloc] init];// 开始执行[subOperation1 start];[subOperation2 start];
  • 3.NSOperationQueue基本使用

    • 两种队列
      • 主队列 通过mainQueue获得,凡是放到主队列中的任务都将在主线程执行
      • 非主队列 直接alloc init出来的队列。非主队列同时具备了并发和串行的功能,通过设置最大并发数属性来控制任务是并发执行还是串行执行
    • 相关代码:

      // 1.创建队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.将操作添加到队列中(会自动调用start方法)[queue addOperation:bOpertion1];//简便方法:该方法内部会自动将block块里面的任务封装为一个NSBlockOperation对象,然后添加到队列[queue addOperationWithBlock:^{    // 代码}];
  • 4.NSOperation的其它用法

    • 4.1 设置最大并发数(控制任务并发和串行)

      // 注意点:该属性需要在任务添加到队列中之前进行设置// 该属性控制队列串行还是并发执行// 如果该属性设置1,则为串行,大于1则就是并发的// 系统默认的值位-1,如果该属性设置为0,则不会执行任何任务queue.maxConcurrentOperationCount = 2;
    • 4.2 暂停.恢复以及取消

      // 暂停,只是不执行当前任务下面的操作,但是当前的还是会执行self.queue.suspended = YES;// 恢复self.queue.suspended = NO;// 取消队列中的所有操作,不可以恢复[self.queue cancelAllOperations];// 注意:苹果官方建议,每当执行完一次耗时操作之后,就查看一下当前队列是否为取消状态,如果是,那么就直接退出// 好处是可以提高程序的性能if (self.isCancelled) {    return;}
  • 5.操作依赖和操作监听

    // op1和op2都是NSOperation操作,能保证操作1依赖于操作2,执行2再执行1// 如果互相都依赖对方,那么两个都不依赖[op1 addDependency:op2];
  • 6.小案例

    • 6.1 下载图片
    看附件
    • 6.2 图片下载综合案例
    看附件// 1.数据展示// 2.内存存储(存储图片)NSMutableDictionary *iamges// 3.沙盒存储(Libriary/caches)NSString *caches = [NSSearchPathForDirectoriesInDimains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];// 4.问题1:数据混乱(重复下载)-->将操作加到操作缓存中// 问题2:复用问题:判断之前设置占位图片self.imageView.image = nil;// 问题3:数据容错处理// 发生内存警告:1.清空内存缓存 2.关闭所有的队列操作 3.清空所有的下载操作字典
  • 7.附录

    • addEd..内部调用的是start方法,start内部调用的是main

转载于:https://www.cnblogs.com/LongLJ/p/5084341.html

你可能感兴趣的文章
Composer的基本使用
查看>>
CocoaPods安装使用及配置私有库
查看>>
C++ format库
查看>>
ELK 环境准备---jre 1.8
查看>>
Redis学习笔记---介绍、安装和通用命令
查看>>
dom4j的生成xml并格式化输出
查看>>
Re-negotiation handshake failed: Not accepted b...
查看>>
价值百万的PPT是如何炼成的
查看>>
企业管理过程信息化自助开发平台架构研究与应用
查看>>
TDBadgedCell
查看>>
HMLabel
查看>>
为Redis配置自定义fastJson序列化工具类
查看>>
2015年用户界面工具干货资源精选
查看>>
开源 java CMS - FreeCMS2.3会员我的评论
查看>>
git diff 颜色插件
查看>>
Redis Sentinel 介绍
查看>>
配置SSH连接GitHub
查看>>
phpQuery—基于jQuery的PHP实现
查看>>
Linux下设置环境JDK环境变量
查看>>
Jsoup 输入汇总
查看>>