扩展--控制线程通信
由于线程的调度是透明的,程序有时候很难对它进行有效的控制,为了解决这个问题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