iOS开发之多线程

1 篇文章 0 订阅
1 篇文章 0 订阅

iOS开发之多线程

在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算。可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行。但是机器码是按顺序执行的,一个复杂的多步操作只能一步步按顺序逐个执行。改变这种状况可以从两个角度出发:对于单核处理器,可以将多个步骤放到不同的线程,这样一来用户完成UI操作后其他后续任务在其他线程中,当CPU空闲时会继续执行,而此时对于用户而言可以继续进行其他操作;对于多核处理器,如果用户在UI线程中完成某个操作之后,其他后续操作在别的线程中继续执行,用户同样可以继续进行其他UI操作,与此同时前一个操作的后续任务可以分散到多个空闲CPU中继续执行(当然具体调度顺序要根据程序设计而定),及解决了线程阻塞又提高了运行效率。苹果从iPad2 开始使用双核A5处理器(iPhone中从iPhone 4S开始使用),A7中还加入了协处理器,如何充分发挥这些处理器的性能确实值得思考。今天将重点分析iOS多线程开发。

多线程概述

程序:由源代码生成的可执行的应用。(例如:QQ.app)
进程:一个正在运行的程序可以看做一个进程。(例如:正在运行的QQ就是一个进程),进程拥有独立运行所需的全部资源。
线程:程序中独立运行的代码段。(例如:接收QQ消息的代码)
一个进程是由一个或多个线程组成。进程只负责资源的调度和分配,线程才是程序的真正的执行单元,负责代码的执行。
单线程:每个正在运行的程序(即进程),至少包含一个线程,这个线程叫主线程,主线程在程序启动时被创建,用于执行main函数。只有一个主线程的程序,称作单线程程序。主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行。
多线程:拥有多个线程的程序,称作多线程程序,iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程称作子线程。可以根据需要开辟若干子线程,子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行。
单、多线程区别:单线程程序只有一个线程,代码顺序执行,容易出现代码阻塞(页面假死)。多线程程序有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能。注意:iOS中关于UI的添加和刷新必须在主线程中操作。

NSObject方法

创建一个工程,进行简单的布局:
这里写图片描述
创建一个代码段,它执行需要一定时间,如果不使用多线程,则会出现页面假死。

#pragma  mark —— snippet 代码段
- (void)snippet
{

    /* NSThread 类, 线程类 */

    /* 常用API */
    /* 获取当前的线程 */

    NSThread *current = [NSThread currentThread];
    NSLog(@"snippet's thread: %@",current);

    /*判断当前的线程是否为主线程 */
    BOOL result = [NSThread isMainThread];
        NSLog(@"snippet is mainThread ?: %d",result);

    /* 获取主线程 */
    NSThread *mainThread = [NSThread mainThread];
    NSLog(@"当前的主线程是: %@",mainThread);

    /* 让线程休眠(秒) 再运行 */
//    [NSThread sleepForTimeInterval:2];


    /* 执行的代码段 */
    NSInteger a = 0;
    for (int i = 0; i < 1000000000; i++) {
        a = a + i;
//        NSLog(@"%ld",a);
    }
    NSLog(@"a = %ld",a);

}

#pragma  mark —— 知识点1 多线程方法1: NSObject方法
- (IBAction)NSObject:(id)sender
{

    NSLog(@"NSObject Thread :%@",[NSThread currentThread]);
    NSLog(@"NSObject isMainThread ?: %d",[NSThread isMainThread]);

    /* 使用NSObject 方法, 将执行的代码放在后台线程运行(即另一个子线程) */
    [self performSelectorInBackground:@selector(snippet) withObject:nil];
}

NSThread

NSThread是轻量级的多线程开发,使用起来也并不复杂,但是使用NSThread需要自己管理线程生命周期。可以使用对象方法+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument直接将操作添加到线程中并启动,也可以使用对象方法- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument 创建一个线程对象,然后调用start方法启动线程。

#pragma  mark —— 知识点2 多线程方法2: NSThread
- (IBAction)NSThread:(id)sender
{

    NSLog(@"NSThread Thread :%@",[NSThread currentThread]);
    NSLog(@"NSThread isMainThread ?: %d",[NSThread isMainThread]);
    /* NSThread 线程类, 一个对象相当于一个线程 */

    /*NSThread 方法一: */
    /* 创建对象 */
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(snippet) object:nil];

    /* 设置线程名字 */
    thread1.name = @"aaaa";

    /* 开始*/
    [thread1 start];

    /*NSThread 方法二: */
    [NSThread detachNewThreadSelector:@selector(snippet) toTarget:self withObject:nil];

}

NSOperation

NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类。NSOperation只是一个操作,本身无主线程、子线程之分,可在任意线程中使用。通常与NSOperationQueue结合使用。使用NSOperation和NSOperationQueue进行多线程开发类似于C#中的线程池,只要将一个NSOperation(因为它是抽象类,实际开中需要使用其子类NSInvocationOperation、NSBlockOperation)放到NSOperationQueue这个队列中线程就会依次启动。NSOperationQueue负责管理、执行所有的NSOperation,在这个过程中可以更加容易的管理线程总数和控制线程之间的依赖关系。
NSOperation有两个常用子类用于创建线程操作:NSInvocationOperation和NSBlockOperation,两种方式本质没有区别,但是是后者使用Block形式进行代码组织,使用相对方便。

#pragma  mark —— 知识点3 多线程方法3: NSOperation
- (IBAction)NSOperation:(id)sender
{

    /* NSOperation 类, 它是一个基类, 抽象类, 不能直接创建对象, 用它的子类或 系统推荐的两个类 创建对象
     * 它的子类对象, 相当于一个任务(要执行的代码段)
     * 原理: 将任务放入当前线程中执行
     */

    /* 系统推荐子类 */
    //NSInvocationOperation
    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(snippet) object:nil];

    [invocation start];

    //NSBlockOperation
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [self snippet];
    }];

    [blockOperation start];

}
#pragma  mark ** NSOperationQueue 实现多线程
- (IBAction)NSOperationQueue:(id)sender
{
    /* NSOperationQueue 类, 任务队列
     * 原理: 队列有一个线程池. 队列负责把任务分配给线程, 当线程执行完之后, 回到线程池, 等待下一次任务
     */

    /* 创建对象 */
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    /* 设置最大并发数 */
    queue.maxConcurrentOperationCount = 2;

    /* 队列中添加NSOperation(任务) */
    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(snippet) object:nil];

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [self snippet];
    }];

    [queue addOperation:invocation];
    [queue addOperation:blockOperation];

    [queue addOperationWithBlock:^{
        [self snippet];
    }];

    [queue addOperationWithBlock:^{
        [self snippet];
    }];

}

GCD

GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法。几种开发中GCD抽象层次最高,当然是用起来也最简单,只是它基于C语言开发,并不像NSOperation是面向对象的开发,而是完全面向过程的。对于熟悉C#异步调用的朋友对于GCD学习起来应该很快,因为它与C#中的异步调用基本是一样的。这种机制相比较于前面两种多线程开发方式最显著的优点就是它对于多核运算更加有效。
GCD中也有一个类似于NSOperationQueue的队列,GCD统一管理整个队列中的任务。但是GCD中的队列分为并行队列和串行队列两类:
串行队列:只有一个线程,加入到队列中的操作按添加顺序依次执行。
并发队列:有多个线程,操作进来之后它会将这些队列安排在可用的处理器上,同时保证先进来的任务优先处理。
其实在GCD中还有一个特殊队列就是主队列,用来执行主线程上的操作任务(从前面的演示中可以看到其实在NSOperation中也有一个主队列)。

#pragma  mark —— 知识点4 多线程方法4: GCD
- (IBAction)GCD:(id)sender
{
    /* GCD: 大调度中心 
     * 函数级别, 效率高. 推荐使用
     * 原理: 有一个队列, 队列中是代码段任务(任务的创建使用block或者函数)
     * 队列, 可以自定义队列, 也可以使用系统的队列
     * 队列分: 串行和并行(并发)两种
     */

#if 0 /* 创建自定义队列 */

    /* 1. 创建一个串行调度队列, 串行即, 任务一个一个执行 */
    dispatch_queue_t myQueue = dispatch_queue_create("com.myQueue.www", DISPATCH_QUEUE_SERIAL);

    /* 2. 调度队列中添加任务 */
    dispatch_async(myQueue, ^{
        [self snippet];
    });

    dispatch_async(myQueue, ^{
        [self snippet];
    });

    dispatch_async(myQueue, ^{
        [self snippet];
    });

#endif

#if 0 /* 创建自定义并发调度队列*/

    dispatch_queue_t myConQueue = dispatch_queue_create("com.myConQueue.www", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(myConQueue, ^{
        [self snippet];
    });

    dispatch_async(myConQueue, ^{
        [self snippet];
    });

    dispatch_async(myConQueue, ^{
        [self snippet];
    });


#endif


#pragma  mark ** 系统的队列
    /* 
     * 系统是有一个串行队列: main , 即主线程
     * 有四个并发队列:high, default, low, background. 通常使用default
     */

#if 0 /* 系统的串行队列 */

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    /* 向调度队列添加任务 */
    dispatch_async(mainQueue, ^{
        [self snippet];
    });

    dispatch_async(mainQueue, ^{
        [self snippet];
    });

    dispatch_async(mainQueue, ^{
        [self snippet];
    });


#endif

#if 0 /* 系统的并发队列*/

    dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(global, ^{
        [self snippet];
    });

    dispatch_async(global, ^{
        [self snippet];
    });

    dispatch_async(global, ^{
        [self snippet];
    });


#endif

#if 1
#pragma  mark ** 重点 GCD的使用
    /* 子线程负责数据的加载
     * 主线程负责刷新等操作
     */

    dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(global, ^{
        /* 数据加载 */
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/42a98226cffc1e1786934a384890f603738de98f.jpg"]]];

//        /* 回到主线程队列, 添加任务(刷新任务) */
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });

    });
#endif

#if 0 /* 延时执行 */

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self snippet];
    });


#endif

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值