• [ios2]使用NSOperationQueue简化多线程开发和队列的优先级 【转】


    多线程开发是一件需要特别精心的事情,即使是对有多年开发经验的工程师来说。

    为了能让初级开发工程师也能使用多线程,同时还要简化复杂性。各种编程工具提供了各自的办法。对于iOS来说,建议在尽可能的情况下避免直接操作线程,使用比如NSOperationQueue这样的机制。

    可以把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。

    你可以设置线程池中只有一个线程,这样,各个操作就可以认为是近似的顺序执行了。为什么说是近似呢,后面会做解释。

     

    编写最简单的示例

    先写个最简单的示例。

     

    编写一个NSOperation的子类,只需实现main方法。这里非常类似Java的Thread,你可以继承它,并覆盖run方法,在该方法里面写入需要执行的代码。这里的main方法和run方法作用是相似的。

    头文件:

    @interface MyTask : NSOperation {
        int operationId;
    }

    @property int operationId;

    @end

    这里的operationId属性不是必须的,是我想在后面标识区分多个Task的标识位。

    m文件:

    @implementation MyTask

    @synthesize operationId;

    - (void)main{
        NSLog(@"task %i run … ",operationId);
        [NSThread sleepForTimeInterval:10];
        NSLog(@"task %i is finished. ",operationId);
    }

    @end

    这里模拟了一个耗时10秒钟的操作。

    下面需要把Task加入到队列中:

    - (void)viewDidLoad {
        [super viewDidLoad];
        queue=[[NSOperationQueue alloc] init];
       
        int index=1;
        MyTask *task=[[[MyTask alloc] init] autorelease];
        task.operationId=index++;
            
        [queue addOperation:task];

    我直接找了个Controller的方法写上了。运行结果是,界面出现了,而task还未执行完,说明是多线程的。10秒钟后,日志打印完毕,类似这样:

    2011-07-18 15:59:14.622 MultiThreadTest[24271:6103] task 1 run …
    2011-07-18 15:59:24.623 MultiThreadTest[24271:6103] task 1 is finished.

    可以向操作队列(NSOperationQueue)增加多个操作,比如这样:

    - (void)viewDidLoad {
        [super viewDidLoad];
        queue=[[NSOperationQueue alloc] init];
       
        int index=1;
        MyTask *task=[[[MyTask alloc] init] autorelease];
        task.operationId=index++;    
        [queue addOperation:task];
       
        task=[[[MyTask alloc] init] autorelease];
        task.operationId=index++;

        [queue addOperation:task];
    }

    那么打印出的内容是不定的,有可能是这样:

    2011-07-18 15:49:48.087 MultiThreadTest[24139:6203] task 1 run …
    2011-07-18 15:49:48.087 MultiThreadTest[24139:1903] task 2 run …
    2011-07-18 15:49:58.122 MultiThreadTest[24139:6203] task 1 is finished.
    2011-07-18 15:49:58.122 MultiThreadTest[24139:1903] task 2 is finished.

    甚至有可能是这样:

    2011-07-18 15:52:24.686 MultiThreadTest[24168:1b03] task 2 run …
    2011-07-18 15:52:24.685 MultiThreadTest[24168:6003] task 1 run …
    2011-07-18 15:52:34.708 MultiThreadTest[24168:1b03] task 2 is finished.
    2011-07-18 15:52:34.708 MultiThreadTest[24168:6003] task 1 is finished.

    因为两个操作提交的时间间隔很近,线程池中的线程,谁先启动是不定的。

    那么,如果需要严格意义的顺序执行,怎么办呢?

     

    处理操作之间的依赖关系

    如果操作直接有依赖关系,比如第二个操作必须等第一个操作结束后再执行,需要这样写:

    queue=[[NSOperationQueue alloc] init];

    int index=1;
    MyTask *task=[[[MyTask alloc] init] autorelease];
    task.operationId=index++;

    [queue addOperation:task];

    task=[[[MyTask alloc] init] autorelease];
    task.operationId=index++;

    if ([[queue operations] count]>0) {
        MyTask *theBeforeTask=[[queue operations] lastObject];
        [task addDependency:theBeforeTask];
    }

    [queue addOperation:task];

    这样,即使是多线程情况下,可以看到操作是严格按照先后次序执行的。

     

    控制线程池中的线程数

    可以通过类似下面的代码:

    [queue setMaxConcurrentOperationCount:2];

    来设置线程池中的线程数,也就是并发操作数。默认情况下是-1,也就是没有限制,同时运行队列中的全部操作。


     

    队列的优先级

    使用NSOperationQueue简化多线程开发中介绍了iOS的操作队列使用方法。这里补充一下队列的优先级功能。

    假设现在队列中有多个操作(NSOperation),现在再向队列中加入新的操作,并且希望新增加的操作排在未执行操作的最前面。这时就需要设置操作的优先级了。

    编写了个最简单的NSOperation,头文件:

    #import <Foundation/Foundation.h>

    @interface MyOperation : NSOperation{
        NSString *name;
    }

    @property (nonatomic,retain) NSString *name;

    @end

     

    实现文件:

    #import "MyOperation.h"

    @implementation MyOperation

    @synthesize name;

    -(void) main{
        NSLog(@"run operation: %@",name);
        [NSThread sleepForTimeInterval:3];
    }

    @end

    在控制器里创建3个实例:

    - (void)viewDidLoad {
        [super viewDidLoad];
        queue=[[NSOperationQueue alloc] init];
        [queue setMaxConcurrentOperationCount:1];
       
        MyOperation *o1=[[[MyOperation alloc] init] autorelease];
        o1.name=@"o1";
        [queue addOperation:o1];
       
        MyOperation *o2=[[[MyOperation alloc] init] autorelease];
        o2.name=@"o2";
        [queue addOperation:o2];
       
        [NSThread sleepForTimeInterval:1];
       
        MyOperation *o3=[[[MyOperation alloc] init] autorelease];
        o3.name=@"o3";
        [o3 setQueuePriority:NSOperationQueuePriorityHigh];
        [queue addOperation:o3];

    运行效果:

    2011-07-26 14:47:23.822 NSOperationQueueDemo[27495:6003] run operation: o1
    2011-07-26 14:47:26.823 NSOperationQueueDemo[27495:6003] run operation: o3
    2011-07-26 14:47:29.824 NSOperationQueueDemo[27495:6203] run operation: o2

    可见,操作3成功的插到第一个等待操作(操作2)之前执行了。

  • 相关阅读:
    解决org.apache.jasper.JasperException: Failed to load or instantiate TagLibraryVal
    学java快2月了,对其应该有点清晰的认识了
    Linux(CentOS)挂载移动硬盘
    SEVERE: A child container failed during start
    JSTL 标签 详解
    转载自MSDN:Implementing a Membership Provider
    一个比较实用的服务器端模拟客户端Alert的代码
    简单的SQL分页法
    转载:Global.asax 文件 使用参考
    转载:缓存 Cache
  • 原文地址:https://www.cnblogs.com/jinjiantong/p/3209834.html
Copyright © 2020-2023  润新知