简单的IOS生产者-消费者模型

时间:2022-08-28 23:30:28

扩展--控制线程通信

由于线程的调度是透明的,程序有时候很难对它进行有效的控制,为了解决这个问题iOS提供了NSCondition来控制线程通信(同前面GCD的信号机制类似)。NSCondition实现了NSLocking协议,所以它本身也有lock和unlock方法,因此也可以将它作为NSLock解决线程同步问题,此时使用方法跟NSLock没有区别,只要在线程开始时加锁,取得资源后释放锁即可,这部分内容比较简单在此不再演示。当然,单纯解决线程同步问题不是NSCondition设计的主要目的,NSCondition更重要的是解决线程之间的调度关系(当然,这个过程中也必须先加锁、解锁)。NSCondition可以调用wati方法控制某个线程处于等待状态,直到其他线程调用signal(此方法唤醒一个线程,如果有多个线程在等待则任意唤醒一个)或者broadcast(此方法会唤醒所有等待线程)方法唤醒该线程才能继续。

假设当前imageNames没有任何图片,而整个界面能够加载15张图片(每张都不能重复),现在创建15个线程分别从imageNames中取图片加载到界面中。由于imageNames中没有任何图片,那么15个线程都处于等待状态,只有当调用图片创建方法往imageNames中添加图片后(每次创建一个)并且唤醒其他线程(这里只唤醒一个线程)才能继续执行加载图片。如此,每次创建一个图片就会唤醒一个线程去加载,这个过程其实就是一个典型的生产者-消费者模式。下面通过NSCondition实现这个流程的控制:


#import "KCNSConditionViewController.h"

#import "KCImageData.h"

#define ROW_COUNT 5

#define COLUMN_COUNT 3

#define ROW_HEIGHT 100

#define ROW_WIDTH ROW_HEIGHT

#define CELL_SPACING 10

#define IMAGE_COUNT 9


@interface KCNSConditionViewController ()

{

    NSMutableArray *_imageViews;

    NSCondition *_condition;

}


@end


@implementation KCNSConditionViewController


- (void)viewDidLoad {

    [superviewDidLoad];

    // Do any additional setup after loading the view.

    [selflayoutUI];

}


#pragma mark - 内部私有方法

#pragma mark 界面布局

-(void)layoutUI{

    //创建多个图片控件用于显示图片

    _imageViews=[NSMutableArrayarray];

    for (int r=0; r<ROW_COUNT; r++) {

        for (int c=0; c<COLUMN_COUNT; c++) {

            UIImageView *imageView=[[UIImageViewalloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING                           ),ROW_WIDTH, ROW_HEIGHT)];

            imageView.contentMode=UIViewContentModeScaleAspectFit;

            [self.viewaddSubview:imageView];

            [_imageViewsaddObject:imageView];

            

        }

    }

    

    UIButton *btnLoad=[UIButtonbuttonWithType:UIButtonTypeRoundedRect];

    btnLoad.frame=CGRectMake(50,500, 100,25);

    [btnLoad setTitle:@"加载图片"forState:UIControlStateNormal];

    [btnLoad addTarget:selfaction:@selector(loadImageWithMultiThread)forControlEvents:UIControlEventTouchUpInside];

    [self.viewaddSubview:btnLoad];

    

    UIButton *btnCreate=[UIButtonbuttonWithType:UIButtonTypeRoundedRect];

    btnCreate.frame=CGRectMake(160,500, 100,25);

    [btnCreate setTitle:@"创建图片"forState:UIControlStateNormal];

    [btnCreate addTarget:selfaction:@selector(createImageWithMultiThread)forControlEvents:UIControlEventTouchUpInside];

    [self.viewaddSubview:btnCreate];

    

    //创建图片链接

    _imageNames=[NSMutableArrayarray];

    

    //初始化锁对象

    _condition=[[NSConditionalloc]init];

    

    _currentIndex=0;

    

}



#pragma mark 创建图片

-(void)createImageName{

    [_conditionlock];

    //如果当前已经有图片了则不再创建,线程处于等待状态

    if (_imageNames.count>0) {

        NSLog(@"createImageName wait, current:%i",_currentIndex);

        [_conditionwait];

    }else{

        NSLog(@"createImageName work, current:%i",_currentIndex);

        //生产者,每次生产1张图片

        [_imageNamesaddObject:[NSStringstringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",_currentIndex++]];

        

        //创建完图片则发出信号唤醒其他等待线程

        [_conditionsignal];

    }

    [_conditionunlock];

}


#pragma mark 加载图片并将图片显示到界面

-(void)loadAnUpdateImageWithIndex:(int )index{

    //请求数据

    NSData *data= [selfrequestData:index];

    //更新UI界面,此处调用了GCD主线程队列的方法

    dispatch_queue_t mainQueue=dispatch_get_main_queue();

    dispatch_sync(mainQueue, ^{

        UIImage *image=[UIImageimageWithData:data];

        UIImageView *imageView=_imageViews[index];

        imageView.image=image;

    });

}


#pragma mark 请求图片数据

-(NSData *)requestData:(int )index{

    NSData *data;

    NSString *name;

    name=[_imageNameslastObject];

    [_imageNamesremoveObject:name];

    if(name){

        NSURL *url=[NSURLURLWithString:name];

        data=[NSDatadataWithContentsOfURL:url];

    }

    return data;

}


#pragma mark 加载图片

-(void)loadImage:(NSNumber *)index{

    int i=(int)[indexintegerValue];

    //加锁

    [_conditionlock];

    //如果当前有图片资源则加载,否则等待

    if (_imageNames.count>0) {

        NSLog(@"loadImage work,index is %i",i);

        [selfloadAnUpdateImageWithIndex:i];

        [_conditionbroadcast];

    }else{

        NSLog(@"loadImage wait,index is %i",i);

        NSLog(@"%@",[NSThreadcurrentThread]);

        //线程等待

        [_conditionwait];

        NSLog(@"loadImage resore,index is %i",i);

        //一旦创建完图片立即加载

        [selfloadAnUpdateImageWithIndex:i];

    }

    //解锁

    [_conditionunlock];

}



#pragma mark - UI调用方法

#pragma mark 异步创建一张图片链接

-(void)createImageWithMultiThread{

    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    //创建图片链接

    dispatch_async(globalQueue, ^{

        [selfcreateImageName];

    });

}


#pragma mark 多线程下载图片

-(void)loadImageWithMultiThread{

    int count=ROW_COUNT*COLUMN_COUNT;

    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    

    for (int i=0; i<count; ++i) {

        //加载图片

        dispatch_async(globalQueue, ^{

            [selfloadImage:[NSNumbernumberWithInt:i]];

        });

    }

}


- (void)didReceiveMemoryWarning {

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}



@end