NSOperationQueue高级运用:管理和调度里面线程池

来源:互联网 发布:摇钱树是什么软件 编辑:程序博客网 时间:2024/05/16 18:22

    当你使用NSOperationQueue的时候,怎么按照你自己的意愿去管理每一个你add进去的NSOperation(进程的优先级,相同线程请求操作的新旧替换


阅读本文需要理解KVO && KVC(个人认为,如果想成为一个越来越好的IOS developer,这个是必须要懂得的,还有core data)


一.demo(多线程下载图片):控制NSOperation执行的优先级,并且限制最大线程执行数目,若已经达到最大线程执行数目,又有新的线程进入队列,那么停止最老的一个线程,把他的位置让给最新进入的线程:

(为什么有最大线程个数的限制:主要考虑的是在移动网络的情况下,网络上行和下行带宽的限制,给用户一个较好的体验,而不是闲线程过多时消耗过大,毕竟IOS不是MTK~)

1.自定义一个自己的NSOperationQueue:主要为了在NSOperationQueue init的时候加入一行代码

-(id)init

{

    if (self = [superinit]) {

        [selfsetMaxConcurrentOperationCount:10];

        [selfaddObserver:selfforKeyPath:@"operations"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:nil];

    }

    returnself;

}


2.上行代码让你监控了NSOperationQueue 中的 NSOperation NSArray的变化,当有变化的时候,你会收到消息

我下面的代码是写一个(后进先执行的 进程队列管理)

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"operations"])
    {
        NSArray *oldOperation = [change objectForKey:@"old"];//变化之前的NSOperation Array
        NSArray *newOperation = [change objectForKey:@"new"];//新的的NSOperation Array
 
//!注意A:此时队列里的所有线程仍然在执行,所以你此时取出的线程新旧数组,仅仅是一个参考值(他们可能其中一个在此时已经执行完毕了)


//第一个NSOperation加入,无需处理
        if (newOperation.count == 1) {
            return;
        }
        
        //进入的线程个数没有超过你设定的最大线程个数,所以无需管理他的优先级,因为所有的线程都在运行
        if (self.operationCount <= self.maxConcurrentOperationCount) {
            NSLog(@"add one operation to queue.do not need to change operation priority level");
            return;
        }
        //进入线程队列的线程个数过多,所以停止最老的一个线程,执行最新的线程
        if (newOperation.count > oldOperation.count) {
            //先停止整个线程执行队列:若不停止,你这段代码执行的同时,其余线程仍然在执行,此时你所取的一些状态值只是一个参考值,很有可能在你代码执行了2行后,有一个线程就执行完毕了!
            [self setSuspended:YES];
            NSOperation *operation;
   //需要停止的线程个数
            int needcancelNum = self.operationCount - self.maxConcurrentOperationCount;
            int i = 0;
            for (operation in newOperation) {
                if ([operation isFinished]) {
   //一个线程已经执行完毕:为什么:参照 注意A
                    needcancelNum--;
                    NSLog(@"%@ is finished", operation);
                }else if(i < needcancelNum)
                {
   //取消最旧的线程执行
                    [operation cancel];
                    NSLog(@"%@ is canceld", operation);
                    i++;
                }else
                {
   //将其余所有线程的优先级设置为0.8
                    operation.threadPriority = 0.8;
                }
            }
   //此时operation 是最新的一个线程,将其优先级设置为1.0(最高)
            operation.threadPriority = 1.0;
   //恢复线程队列的运行
            [self setSuspended:NO];
        }
    }
}


二:相同线程请求操作的新旧替换

如何识别一个线程的功能是相同的

(使用场景:由于用户的刷新操作,你需要下载最新网络数据对应的图片,此时若前一次网络需要下载的图片还没有下载完成,你需要用你最新的线程去替换旧的线程)

我想到的方案是,用一个自己定义过的NSOperation,在里面加入一个属性,线程名字。

这样在上文所述 线程队列的NSOperation数组发生了变化时,你可以查询所有线程的名字,然后检测下是否有相同名字的线程进入了线程队列,如果有的话,你需要取消旧的那个线程。

一样在

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 中加入以下代码:   PicLoadAndCacheOperation 是一个custom NSOperation


//new operation comes more than 1,so check Did their have the same name
        if((newOperation.count - oldOperation.count) > 1)
        {
            [self setSuspended:YES];
            for (int i = 0; i < newOperation.count; i++)
            {
                PicLoadAndCacheOperation *keyOperation = [newOperation objectAtIndex:i];
                
                for (int j = i + 1; j < newOperation.count; j++)
                {
                    PicLoadAndCacheOperation *comparedOperation = [newOperation objectAtIndex:j];
                    if ([comparedOperation.operationName isEqualToString:keyOperation.operationName])
                    {
                        [keyOperation cancel];
                        continue;
                    }
                }
            }
            [self setSuspended:NO];
        }
        //instead last thread by new thread by it's operation name
        else if ((newOperation.count - oldOperation.count) == 1) {
            [self setSuspended:YES];
            PicLoadAndCacheOperation *Operation = [newOperation objectAtIndex:oldOperation.count];
            NSString *newOperationName = Operation.operationName;
            
            for (PicLoadAndCacheOperation *oOperation in oldOperation) {
                NSString *oldOperationName = oOperation.operationName;
                if ([oldOperationName isEqualToString:newOperationName]) {
                    [oOperation cancel];
                    [self setSuspended:NO];
                    return;
                }
            }
            [self setSuspended:NO];
        }

原创粉丝点击