iOS多线程总览 --By 吴帮雷

在iOS中每个进程启动后都会建立一个主线程(UI线程),这个线程是其他线程的父线程。由于在iOS中除了主线程,其他子线程是独立于Cocoa Touch的,所以只有主线程可以更新UI界面(新版iOS中,使用其他线程更新UI可能也能成功,但是不推荐)。iOS中多线程使用并不复杂,关键是如何控制好各个线程的执行顺序、处理好资源竞争问题。常用的多线程开发有三种方式:

1.NSThread

2.NSOperation

3.GCD

三种方式是随着iOS的发展逐渐引入的,所以相比而言后者比前者更加简单易用,并且GCD也是目前苹果官方比较推荐的方式(它充分利用了多核处理器的运算性能)。

一 、NSThread是轻量级的多线程开发,使用起来也并不复杂,但是使用NSThread需要自己管理线程生命周期。

//
//  RootViewController.m
//  多线程Demo
//
//  Created by 吴帮雷 on 15/5/22.
//  Copyright (c) 2015年 吴帮雷. All rights reserved.
//

#import "RootViewController.h"
#import "TwoViewController.h"
@interface RootViewController ()
{
    UIImageView *_imageView;
}
@end

@implementation RootViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self initUI];
}
- (void)initUI
{
    _imageView =[[UIImageView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
    _imageView.contentMode=UIViewContentModeScaleAspectFit;
    [self.view addSubview:_imageView];
}
- (IBAction)btnClick:(id)sender {
    UIButton *b=(UIButton *)sender;
    if (b.tag == 1) {
        [self loadImageWithMultiThread];
    }
    else
    {
        TwoViewController *two = [TwoViewController new];
        [self presentViewController:two animated:YES completion:^{

        }];
    }

}
#pragma mark 将图片显示到界面
-(void)updateImage:(NSData *)imageData{
    UIImage *image=[UIImage imageWithData:imageData];
    _imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData{
    //对于多线程操作建议把线程操作放到@autoreleasepool中
    @autoreleasepool {
        NSURL *url=[NSURL URLWithString:@"http://www.sinaimg.cn/dy/slidenews/4_img/2015_21/704_1633803_604865.jpg"];
        NSData *data=[NSData dataWithContentsOfURL:url];
        return data;
    }
}
#pragma mark 加载图片
-(void)loadImage{
    //请求数据
    NSData *data= [self requestData];
    /*将数据显示到UI控件,注意只能在主线程中更新UI,
     另外performSelectorOnMainThread方法是NSObject的分类方法,每个NSObject对象都有此方法,
     它调用的selector方法是当前调用控件的方法,例如使用UIImageView调用的时候selector就是UIImageView的方法
     Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
     waitUntilDone:是否线程任务完成执行
     */
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
    //方法1:使用对象方法
    //创建一个线程,第一个参数是请求的操作,第二个参数是操作方法的参数
    //    NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
    //    //启动一个线程,注意启动一个线程并非就一定立即执行,而是处于就绪状态,当系统调度时才真正执行
    //    [thread start];

    //方法2:使用类方法 直接调用
    [NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

接下来将创建多个UIImageView并创建多个线程用于往UIImageView中填充图片。

//
//  TwoViewController.m
//  多线程Demo
//
//  Created by 吴帮雷 on 15/5/22.
//  Copyright (c) 2015年 吴帮雷. All rights reserved.
//

#define ROW_COUNT 5
#define COLUMN_COUNT 3
#define ROW_HEIGHT 100
#define ROW_WIDTH ROW_HEIGHT
#define CELL_SPACING 10

#import "TwoViewController.h"
#import "ImageModel.h"

@interface TwoViewController ()
{
    NSMutableArray *_imageViews;
}
@end

@implementation TwoViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor lightGrayColor];
    [self initUI];
}
- (void)initUI
{
    //创建多个图片控件用于显示图片
    _imageViews=[NSMutableArray array];
    for (int r=0; r<ROW_COUNT; r++) {
        for (int c=0; c<COLUMN_COUNT; c++) {
            UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING                           ), ROW_WIDTH, ROW_HEIGHT)];
            imageView.contentMode=UIViewContentModeScaleAspectFit;
            [self.view addSubview:imageView];
            [_imageViews addObject:imageView];

        }
    }

    UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame=CGRectMake(50, 500, 220, 25);
    [button setTitle:@"加载图片" forState:UIControlStateNormal];
    //添加方法
    [button addTarget:self action:@selector(loadImageWithMultiThreadForOrder) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}
#pragma mark 将图片显示到界面
-(void)updateImage:(ImageModel *)imageData{
    UIImage *image=[UIImage imageWithData:imageData.data];
    UIImageView *imageView= _imageViews[imageData.index];
    imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
    //对于多线程操作建议把线程操作放到@autoreleasepool中
    @autoreleasepool {
        NSURL *url=[NSURL URLWithString:@"http://campus-belles.biyixia.com/sites/campus-belles.biyixia.com/files/styles/medium/public/files/images/product/fashion/tv/campus_belles/886.png?itok=vqvr5yY2"];
        NSData *data=[NSData dataWithContentsOfURL:url];
        return data;
    }
}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
    //currentThread方法可以取得当前操作线程
    int i=[index integerValue];

     NSLog(@"\n当前加载图片%i",i);//未必按顺序输出

    NSData *data= [self requestData:i];

    ImageModel *imageData=[[ImageModel alloc]init];
    imageData.index=i;
    imageData.data=data;
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES];
}
#pragma mark 多线程下载图片 优先某一个
-(void)loadImageWithMultiThreadForOrder{
    NSMutableArray *threads=[NSMutableArray array];
    int count=ROW_COUNT*COLUMN_COUNT;
    //创建多个线程用于填充图片
    for (int i=0; i<count; ++i) {
        NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
        thread.name=[NSString stringWithFormat:@"myThread%i",i];//设置线程名称
        if(i==(5)){
            thread.threadPriority=1.0;
        }else{
            thread.threadPriority=0.0;
        }
        [threads addObject:thread];
    }

    for (int i=0; i<count; i++) {
        NSThread *thread=threads[i];
        [thread start];
    }
}
#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
    //创建多个线程用于填充图片
    for (int i=0; i<ROW_COUNT*COLUMN_COUNT; ++i) {
        //        [NSThread detachNewThreadSelector:@selector(loadImage:) toTarget:self withObject:[NSNumber numberWithInt:i]];
        NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
        thread.name=[NSString stringWithFormat:@"myThread%i",i];//设置线程名称
        [thread start];
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

在线程操作过程中可以让某个线程休眠等待,优先执行其他线程操作,而且在这个过程中还可以修改某个线程的状态或者终止某个指定线程。为了解决上面优先加载最后一张图片的问题,不妨让其他线程先休眠一会等待最后一个线程执行。修改图片加载方法如下即可

-(NSData *)requestData:(int )index{
    //对于多线程操作建议把线程操作放到@autoreleasepool中
    @autoreleasepool {
        //对非最后一张图片加载线程休眠2秒
        if (index!=(ROW_COUNT*COLUMN_COUNT-1)) {
            [NSThread sleepForTimeInterval:2.0];
        }
        NSURL *url=[NSURL URLWithString:_imageNames[index]];
        NSData *data=[NSData dataWithContentsOfURL:url];

        return data;
    }
}

假设在图片加载过程中点击停止按钮让没有完成的线程停止加载,可以改造程序如下:

#import "KCMainViewController.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

@interface KCMainViewController (){
    NSMutableArray *_imageViews;
    NSMutableArray *_imageNames;
    NSMutableArray *_threads;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
}

#pragma mark 界面布局
-(void)layoutUI{
    //创建多个图片空间用于显示图片
    _imageViews=[NSMutableArray array];
    for (int r=0; r<ROW_COUNT; r++) {
        for (int c=0; c<COLUMN_COUNT; c++) {
            UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING                           ), ROW_WIDTH, ROW_HEIGHT)];
            imageView.contentMode=UIViewContentModeScaleAspectFit;
//            imageView.backgroundColor=[UIColor redColor];
            [self.view addSubview:imageView];
            [_imageViews addObject:imageView];

        }
    }

    //加载按钮
    UIButton *buttonStart=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    buttonStart.frame=CGRectMake(50, 500, 100, 25);
    [buttonStart setTitle:@"加载图片" forState:UIControlStateNormal];
    [buttonStart addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:buttonStart];

    //停止按钮
    UIButton *buttonStop=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    buttonStop.frame=CGRectMake(160, 500, 100, 25);
    [buttonStop setTitle:@"停止加载" forState:UIControlStateNormal];
    [buttonStop addTarget:self action:@selector(stopLoadImage) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:buttonStop];

    //创建图片链接
    _imageNames=[NSMutableArray array];
    [_imageNames addObject:@    for (int i=0; i<IMAGE_COUNT; i++) {
        [_imageNames addObject:[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",i]];
    }
}

#pragma mark 将图片显示到界面
-(void)updateImage:(KCImageData *)imageData{
    UIImage *image=[UIImage imageWithData:imageData.data];
    UIImageView *imageView= _imageViews[imageData.index];
    imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
    //对于多线程操作建议把线程操作放到@autoreleasepool中
    @autoreleasepool {
        NSURL *url=[NSURL URLWithString:_imageNames[index]];
        NSData *data=[NSData dataWithContentsOfURL:url];

        return data;
    }
}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
    int i=[index integerValue];

    NSData *data= [self requestData:i];

    NSThread *currentThread=[NSThread currentThread];

//    如果当前线程处于取消状态,则退出当前线程
    if (currentThread.isCancelled) {
        NSLog(@"thread(%@) will be cancelled!",currentThread);
        [NSThread exit];//取消当前线程
    }

    KCImageData *imageData=[[KCImageData alloc]init];
    imageData.index=i;
    imageData.data=data;
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES];
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
    int count=ROW_COUNT*COLUMN_COUNT;
    _threads=[NSMutableArray arrayWithCapacity:count];

    //创建多个线程用于填充图片
    for (int i=0; i<count; ++i) {
        NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
        thread.name=[NSString stringWithFormat:@"myThread%i",i];//设置线程名称
        [_threads addObject:thread];
    }
    //循环启动线程
    for (int i=0; i<count; ++i) {
        NSThread *thread= _threads[i];
        [thread start];
    }
}

#pragma mark 停止加载图片
-(void)stopLoadImage{
    for (int i=0; i<ROW_COUNT*COLUMN_COUNT; i++) {
        NSThread *thread= _threads[i];
        //判断线程是否完成,如果没有完成则设置为取消状态
        //注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
        if (!thread.isFinished) {
            [thread cancel];

        }
    }
}
@end
-(void)loadImage:(NSNumber *)index{
    int i=[index integerValue];

    NSData *data= [self requestData:i];

    NSThread *currentThread=[NSThread currentThread];

//    如果当前线程处于取消状态,则退出当前线程
    if (currentThread.isCancelled) {
        NSLog(@"thread(%@) will be cancelled!",currentThread);
        [NSThread exit];//取消当前线程
    }

    KCImageData *imageData=[[KCImageData alloc]init];
    imageData.index=i;
    imageData.data=data;
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES];
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
    int count=ROW_COUNT*COLUMN_COUNT;
    _threads=[NSMutableArray arrayWithCapacity:count];

    //创建多个线程用于填充图片
    for (int i=0; i<count; ++i) {
        NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
        thread.name=[NSString stringWithFormat:@"myThread%i",i];//设置线程名称
        [_threads addObject:thread];
    }
    //循环启动线程
    for (int i=0; i<count; ++i) {
        NSThread *thread= _threads[i];
        [thread start];
    }
}

#pragma mark 停止加载图片
-(void)stopLoadImage{
    for (int i=0; i<ROW_COUNT*COLUMN_COUNT; i++) {
        NSThread *thread= _threads[i];
        //判断线程是否完成,如果没有完成则设置为取消状态
        //注意设置为取消状态仅仅是改变了线程状态而言,并不能终止线程
        if (!thread.isFinished) {
            [thread cancel];

        }
    }
}

NSInvocationOperation

首先使用NSInvocationOperation进行一张图片的加载演示,整个过程就是:创建一个操作,在这个操作中指定调用方法和参数,然后加入到操作队列。其他代码基本不用修改,直接修加载图片方法如下:

-(void)loadImageWithMultiThread{
    /*创建一个调用操作
     object:调用方法参数
    */
    NSInvocationOperation *invocationOperation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
    //创建完NSInvocationOperation对象并不会调用,它由一个start方法启动操作,但是注意如果直接调用start方法,则此操作会在主线程中调用,一般不会这么操作,而是添加到NSOperationQueue中
//    [invocationOperation start];

    //创建操作队列
    NSOperationQueue *operationQueue=[[NSOperationQueue alloc]init];
    //注意添加到操作队后,队列会开启一个线程执行此操作
    [operationQueue addOperation:invocationOperation];
}

GCD

在GCD中提供了一种信号机制,也可以解决资源抢占问题(和同步锁的机制并不一样)。GCD中信号量是dispatch_semaphore_t类型,支持信号通知和信号等待。每当发送一个信号通知,则信号量+1;每当发送一个等待信号时信号量-1,;如果信号量为0则信号会处于等待状态,直到信号量大于0开始执行。根据这个原理我们可以初始化一个信号量变量,默认信号量设置为1,每当有线程进入“加锁代码”之后就调用信号等待命令(此时信号量为0)开始等待,此时其他线程无法进入,执行完后发送信号通知(此时信号量为1),其他线程开始进入执行,如此一来就达到了线程同步目的。

#import "KCMainViewController.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 KCMainViewController (){
    NSMutableArray *_imageViews;
    NSLock *_lock;
    dispatch_semaphore_t _semaphore;//定义一个信号量
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
}

#pragma mark 界面布局
-(void)layoutUI{
    //创建多个图片控件用于显示图片
    _imageViews=[NSMutableArray array];
    for (int r=0; r<ROW_COUNT; r++) {
        for (int c=0; c<COLUMN_COUNT; c++) {
            UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING                           ), ROW_WIDTH, ROW_HEIGHT)];
            imageView.contentMode=UIViewContentModeScaleAspectFit;
            [self.view addSubview:imageView];
            [_imageViews addObject:imageView];

        }
    }

    UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame=CGRectMake(50, 500, 220, 25);
    [button setTitle:@"加载图片" forState:UIControlStateNormal];
    //添加方法
    [button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];

    //创建图片链接
    _imageNames=[NSMutableArray array];
    for (int i=0; i<IMAGE_COUNT; i++) {
        [_imageNames addObject:[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",i]];
    }

    /*初始化信号量
     参数是信号量初始值
     */
    _semaphore=dispatch_semaphore_create(1);

}

#pragma mark 将图片显示到界面
-(void)updateImageWithData:(NSData *)data andIndex:(int )index{
    UIImage *image=[UIImage imageWithData:data];
    UIImageView *imageView= _imageViews[index];
    imageView.image=image;
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
    NSData *data;
    NSString *name;

    /*信号等待
     第二个参数:等待时间
     */
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    if (_imageNames.count>0) {
        name=[_imageNames lastObject];
        [_imageNames removeObject:name];
    }
    //信号通知
    dispatch_semaphore_signal(_semaphore);

    if(name){
        NSURL *url=[NSURL URLWithString:name];
        data=[NSData dataWithContentsOfURL:url];
    }

    return data;
}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
    int i=[index integerValue];
    //请求数据
    NSData *data= [self requestData:i];
    //更新UI界面,此处调用了GCD主线程队列的方法
    dispatch_queue_t mainQueue= dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        [self updateImageWithData:data andIndex:i];
    });
}

#pragma mark 多线程下载图片
-(void)loadImageWithMultiThread{
    int count=ROW_COUNT*COLUMN_COUNT;
//    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //这里创建一个并发队列(使用全局并发队列也可以)
    dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);

    for (int i=0; i<count; i++) {
        dispatch_async(queue, ^{
            [self loadImage:[NSNumber numberWithInt:i]];
        });
    }
}

@end

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

#import "KCMainViewController.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 KCMainViewController (){
    NSMutableArray *_imageViews;
    NSCondition *_condition;
}

@end

@implementation KCMainViewController

#pragma mark - 事件
- (void)viewDidLoad {
    [super viewDidLoad];

    [self layoutUI];
}

#pragma mark - 内部私有方法
#pragma mark 界面布局
-(void)layoutUI{
    //创建多个图片控件用于显示图片
    _imageViews=[NSMutableArray array];
    for (int r=0; r<ROW_COUNT; r++) {
        for (int c=0; c<COLUMN_COUNT; c++) {
            UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING                           ), ROW_WIDTH, ROW_HEIGHT)];
            imageView.contentMode=UIViewContentModeScaleAspectFit;
            [self.view addSubview:imageView];
            [_imageViews addObject:imageView];

        }
    }

    UIButton *btnLoad=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnLoad.frame=CGRectMake(50, 500, 100, 25);
    [btnLoad setTitle:@"加载图片" forState:UIControlStateNormal];
    [btnLoad addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnLoad];

    UIButton *btnCreate=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnCreate.frame=CGRectMake(160, 500, 100, 25);
    [btnCreate setTitle:@"创建图片" forState:UIControlStateNormal];
    [btnCreate addTarget:self action:@selector(createImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnCreate];

    //创建图片链接
    _imageNames=[NSMutableArray array];

    //初始化锁对象
    _condition=[[NSCondition alloc]init];

    _currentIndex=0;

}

#pragma mark 创建图片
-(void)createImageName{
    [_condition lock];
    //如果当前已经有图片了则不再创建,线程处于等待状态
    if (_imageNames.count>0) {
        NSLog(@"createImageName wait, current:%i",_currentIndex);
        [_condition wait];
    }else{
        NSLog(@"createImageName work, current:%i",_currentIndex);
        //生产者,每次生产1张图片
        [_imageNames addObject:[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",_currentIndex++]];

        //创建完图片则发出信号唤醒其他等待线程
        [_condition signal];
    }
    [_condition unlock];
}

#pragma mark 加载图片并将图片显示到界面
-(void)loadAnUpdateImageWithIndex:(int )index{
    //请求数据
    NSData *data= [self requestData:index];
    //更新UI界面,此处调用了GCD主线程队列的方法
    dispatch_queue_t mainQueue= dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        UIImage *image=[UIImage imageWithData:data];
        UIImageView *imageView= _imageViews[index];
        imageView.image=image;
    });
}

#pragma mark 请求图片数据
-(NSData *)requestData:(int )index{
    NSData *data;
    NSString *name;
    name=[_imageNames lastObject];
    [_imageNames removeObject:name];
    if(name){
        NSURL *url=[NSURL URLWithString:name];
        data=[NSData dataWithContentsOfURL:url];
    }
    return data;
}

#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
    int i=(int)[index integerValue];
    //加锁
    [_condition lock];
    //如果当前有图片资源则加载,否则等待
    if (_imageNames.count>0) {
        NSLog(@"loadImage work,index is %i",i);
        [self loadAnUpdateImageWithIndex:i];
        [_condition broadcast];
    }else{
        NSLog(@"loadImage wait,index is %i",i);
        NSLog(@"%@",[NSThread currentThread]);
        //线程等待
        [_condition wait];
        NSLog(@"loadImage resore,index is %i",i);
        //一旦创建完图片立即加载
        [self loadAnUpdateImageWithIndex:i];
    }
    //解锁
    [_condition unlock];
}

#pragma mark - UI调用方法
#pragma mark 异步创建一张图片链接
-(void)createImageWithMultiThread{
    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //创建图片链接
    dispatch_async(globalQueue, ^{
        [self createImageName];
    });
}

#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, ^{
            [self loadImage:[NSNumber numberWithInt:i]];
        });
    }
}
时间: 2025-01-01 07:41:34

iOS多线程总览 --By 吴帮雷的相关文章

iOS - TableViewCell分割线 --By吴帮雷

千万别小看UI中得线,否则你的设计师和测试组会无休止地来找你的!!(如果是美女还好,如果是恐龙....) 在开发中运用最多的是什么,对,表格--TableView,之所以称作表格,是因为他天生带有分割线. 首先系统带的有如下类型: typedef NS_ENUM(NSInteger, UITableViewCellSeparatorStyle) { UITableViewCellSeparatorStyleNone, UITableViewCellSeparatorStyleSingleLine

iOS,开发准备之申请证书 ---by吴帮雷

一.申请真机调试证书 打开iOS Dev Center,选择Sign in,登陆(至少99美元账号),登陆选择Certificates,Identifiers & Profiles --> Identifiers 选择Identifiers->App IDs,开始添加 App ID由用 . 符号分开的两部分组成,在默认情况下,App ID的前缀为你的团队ID,后缀被定义为一个bundle ID的搜索字符串,AppID的每一个部分对你的应用程序都有不同而且很重要的用途 App ID De

NSSet和NSMutableSet - By吴帮雷

1.NSSet的使用 [NSSet setWithSet:(NSSet *)set]; 用另外一个set对象构造 [NSSet setWithArray:(NSArray *)array];用数组构造 [NSSet setWithObjects:...]:创建集合对象,并且初始化集合中的数值,结尾必需使用nil标志. [set count] ; 得到这个结合对象的长度. [set containsObject:...]: 判断这个集合中是否存在传入的对象,返回Bool值. [set object

线程同步-iOS多线程编程指南(四)-08-多线程

首页 编程指南 Grand Central Dispatch 基本概念 多核心的性能 Dispatch Sources 完结 外传:dispatch_once(上) Block非官方编程指南 基础 内存管理 揭开神秘面纱(上) 揭开神秘面纱(下) iOS多线程编程指南 关于多线程编程 线程管理 Run Loop 线程同步 附录 Core Animation编程指南 Core Animation简介 基本概念 渲染架构 几何变换 查看目录 中文手册/API ASIHTTPRequest Openg

iOS多线程杂论

iOS多线程的分布 (1) NSThread (2) NSOperation (3) GCD 现在对下面三个进行一个个的分析,希望那里说得不对的地方希望简友们帮我指点一二. 1,NSThread 优点:NSThread相对比较轻量级缺点:需要自己管理线程生命周期,线程同步,线程同步对数据加锁有一定的系统开销: NSThread 实现的三种方式: 1,NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selecto

iOS多线程知识总结

iOS多线程知识总结 1. iOS中苹果提供4钟方案来帮助我们实现多线程: (1) 纯C语言的pthread,偏底层,需要程序员手动管理线程的生命周期,基本不用. (2) OC语言的NSTread,需要手动管理线程生命周期,偶尔调试用来获取当前线程[NSTread currentTread/mainTread]; (3) 纯C语言的GCD(Grand Central Dispatch伟大的中枢调度器),自动管理线程生命周期,完全隐藏了Tread字眼,面向任务和队列,有同步和异步函数,自动决定开启

iOS多线程开发之深入GCD

iOS多线程开发之深入GCD 一.前言 在以前的一些系列博客中,对iOS中线程的管理做了总结,其中涵盖了GCD的相关基础知识:http://my.oschina.net/u/2340880/blog/417746.那里面将GCD的线程管理能力,列队组能力,通过信号和消息控制程序流程的能力都有介绍,这里,我们继续深入GCD的功能,通过GCD来处理一些逻辑更加复杂的代码功能. 二.延时追加任务 当我们在程序中处理延时任务的时候,我们一般会通过两种方式,一种是通过定时器进行延时执行,另外一种是通过如下

iOS多线程编程Part 1/3 - NSThread &amp; Run Loop

iOS多线程编程Part 1/3 - NSThread & Run Loop 02 JUNE 2013 前言 多线程的价值无需赘述,对于App性能和用户体验都有着至关重要的意义,在iOS开发中,Apple提供了不同的技术支持多线程编程,除了跨平台的pthread之外,还提供了NSThread.NSOperationQueue.GCD等多线程技术,从本篇Blog开始介绍这几种多线程技术的细节. 对于pthread这种跨平台的多线程技术,这本Programming with POSIX Thread

iOS多线程 GCD

iOS多线程 GCD Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取. /*! * @function dispatch_get_main_queue * * @abstract * Returns the default queue that is bound to the main thread. *