iOS 多线程初学 - 多种方法创建多线程

首先介绍几个概念


线程:

  1. 线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行的。

  2. 一个线程中的任务执行是串行的。

  3. 每个进程都有至少一条线程,默认的线程为主线程。


多线程:

  1. 原理:同一时间,CPU 只能处理一条线程,意味着只有一条线程在工作。因为 CPU 的处理速度非常高,所以会产生很多空闲时间(时间碎皮)。多线程并发的原理,其实就是 CPU 快速的在多条线程之间调度。

  2. 优点:提高程序执行效率。提高 CPU,内存等资源的利用效率。

  3. 缺点:开启线程需要占用内存空间(一般主线程占1M,子线程占512K),如果开启大量子线程,会占用大量内存空间,降低系统性能。线程太多,CPU 调度就会非常频繁,加大 CPU 功耗。多线程也会使程序设计更加复杂,因为需要考虑到线程之间的通信和数据共享。


iOS 的开发与多线程:

  一个 iOS 程序运行之后,会创建一条线程,称为“主线程”或者“UI线程”

  主线程主要用于处理UI事件 显示,点击,滚动,拖拽 等等

  耗时操作会卡主主线程,严重影响UI 的流畅度。


iOS 常用线程模型有如下四种

  1. pthread - 纯 C 实现,使用率最低。作为了解即可,日常冷门装逼用。

  2. NSThread - OC 线程库,实用率不高,仅作简单调用,比如获取当前线程什么的。

  3. NSOperationQueue - 线程池/线程队列,实用率高,方便简洁高效。

  4. GCD - Block 模式的线程池,C 实现,项目中也被广泛实用。


下面开实例 ================================

1. phread 简单的创建

需要引入头文件

#import <pthread.h>

pthread_t thread;
pthread_create(&thread, NULL, run, NULL);
第一个参数是声明的线程变量地址,创建的线程会调用第三个函数指针指向的函数。

void * run() {
    NSLog(@"奔跑, 线程: %@", [NSThread currentThread]);
    return NULL;
}

函数中使用了 NSThread 的方法获取了当前的线程并打印出来。

运行结果:

2015-08-28 20:43:31.230 ThreadTest[1773:117469] 奔跑, 线程: <NSThread: 0x7f9f83597c50>{number = 2, name = (null)}
通过结果可以看出,线程的 number 为 2 ,这意味着我们的函数并没有执行在 number 为 0 的主线程上,而是新开辟了一个线程。

2. NSThread
用一个简单的例子来介绍NSThread 的几个常用的属性和方法。

- (void)viewDidLoad {
    [super viewDidLoad];

    NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    NSLog(@"这个输出证明虽然创建了 thread , 但是没有调用 start 就没办法自动执行。");
    //如果需要让线程执行任务,需要调用 start 方法
    [thread start];
}

- (void)run {
    //[NSThread currentThread] 获取当前线程
    NSLog(@"%@", [NSThread currentThread]);
    //[NSThread isMultiThreaded] 判断是否为多线程
    NSLog(@"%d", [NSThread isMultiThreaded]);
    //[NSThread isMainThread] 判断当前线程是否为主线程
    NSLog(@"%d", [NSThread isMainThread]);
}
输出结果如下:

2015-08-28 21:19:23.369 ThreadTest[1864:128703] 这个输出证明虽然创建了 thread , 但是没有调用 start 就没办法自动执行。
2015-08-28 21:19:23.390 ThreadTest[1864:128839] <NSThread: 0x7fb98a496f20>{number = 2, name = (null)}
2015-08-28 21:19:23.391 ThreadTest[1864:128839] 1
2015-08-28 21:19:23.391 ThreadTest[1864:128839] 0
从结果可以得知,在 start 方法被执行后,加入线程的任务被执行,即函数被调用,当前线程是多线程,且不是主线程。

3. GCD

GCD 弥补了 NSThread 难于管理的问题。

GCD 采用队列的方式管理线程

通过 GCD,开发者可以不用和线程打交道,而是直接向队列中扔代码Block 块就可以。

通过一个实例简单实现:

    //创建一个 GCD 队列
    dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);
    dispatch_queue_t myQueue2 = dispatch_queue_create("myQueue2", NULL);
    
    dispatch_async(myQueue, ^{
        NSLog(@"myQueue - currentThread:%@, ",[NSThread currentThread]);
    });
    dispatch_async(myQueue2, ^{
        NSLog(@"myQueue2 - currentThread:%@, ",[NSThread currentThread]);
    });
执行打印结果如下:

2015-08-28 21:37:42.086 ThreadTest[1934:135106] myQueue - currentThread:<NSThread: 0x7fac095bffa0>{number = 2, name = (null)}, 
2015-08-28 21:37:42.086 ThreadTest[1934:135105] myQueue2 - currentThread:<NSThread: 0x7fac09484980>{number = 3, name = (null)}, 
想要执行 GCD 的 dispatch_async 方法,必须把它交给一个队列管理,否则会报错。

dispatch_async 函数会把传入的 block 块放入指定的 queue 里面运行。这个函数是异步的,这就意味着它会立即返回而不管 block 块是否运行结束。

也可以通过获取全局变量的方式:

    //获取全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_async(queue, ^{
        NSLog(@"queue1:%@",[NSThread currentThread]);
    });
    //直接用
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"queue2:%@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"queue3:%@",[NSThread currentThread]);
    });

其中赋值的参数是优先级

运行结果:

2015-08-28 21:46:00.726 ThreadTest[1995:138596] queue2:<NSThread: 0x7fd20158f420>{number = 2, name = (null)}
2015-08-28 21:46:00.728 ThreadTest[1995:138597] queue3:<NSThread: 0x7fd2014ceb20>{number = 3, name = (null)}
2015-08-28 21:46:00.729 ThreadTest[1995:138598] queue1:<NSThread: 0x7fd201583f10>{number = 4, name = (null)}

4. NSOperationQueue

NSOperationQueue的底层就是基于 GCD 实现的,高度分装性使其具备更加易用的好处。

实例:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSInvocationOperation *iop = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
    
    NSBlockOperation *bop = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Block 任务 %@",[NSThread currentThread]);
    }];
    [bop addExecutionBlock:^{
        NSLog(@"新添加的任务 线程 : %@", [NSThread currentThread]);
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //把任务交给队列来执行和管理
    [queue addOperation:iop];
    [queue addOperation:bop];
}

- (void)run {
    NSLog(@"Invocation 任务 线程:%@", [NSThread currentThread]);
}
block方式 可以后续追加任务到 block 中。

执行结果如下:

2015-08-28 21:55:13.282 ThreadTest[2046:142195] Invocation 任务 线程:<NSThread: 0x7ff9e0cdd0b0>{number = 2, name = (null)}
2015-08-28 21:55:13.282 ThreadTest[2046:142194] Block 任务 <NSThread: 0x7ff9e0d7b320>{number = 3, name = (null)}
2015-08-28 21:55:13.282 ThreadTest[2046:142193] 新添加的任务 线程 : <NSThread: 0x7ff9e0d78910>{number = 4, name = (null)}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值