IOS_多线程_NSThread+NSOperation+GCD(Grand Central Dispatcher)

H:/0729/01_对象方法创建NSThread+加锁+售票_ViewController.h
//  ViewController.h
//  卖票
//  Created by apple on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
// 多行文本提示框
@property (weak, nonatomic) IBOutlet UITextView *infoTextView;
// 响应按钮点击,NSThread售票
- (IBAction)threadSales:(id)sender;
@end

H:/0729/01_对象方法创建NSThread+加锁+售票_ViewController.m
//  ViewController.m
//  卖票
//  Created by apple on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import "ViewController.h"
@interface ViewController ()
{
    NSInteger   _tickets;
    // 全局要用到线程锁,是不能够定义在线程方法里面的
    NSLock      *_lock;
}
@end
@implementation ViewController
// 为了在TextView里面增加文本,更新界面,需要先写一个方法处理。
// 1. 读出当前文本框的内容
// 2. 把要追加的文本附加在当前文本框内容的后面
// 3. 重新为文本框赋值
// 这是负责更新UI界面的
- (void)appendTextView:(NSString *)text
{
	//先得到UI中的文本框内容
    NSMutableString *str = [NSMutableString stringWithString:_infoTextView.text];
	//追加参数的内容
    [str appendFormat:@"\n%@", text];
    NSLog(@"after append, text %@", str);
	//重新更新UI文本框内的东西
    [_infoTextView setText:str];
    // 用来将文本框滚动到想要的位置
    // 我们现在想要滚动到最后,这个方法的参数是一个NSRange
    // 定义一个NSRange
    NSRange range = NSMakeRange(str.length, 1);
    [_infoTextView scrollRangeToVisible:range];
}
#pragma mark - NSThread售票
// NSThread卖票实际方法,线程跑的方法
- (void)threadSaleMethod
{
    // 1. 判断是否还有票
    // 2. 更新界面,提示当前票数
    // 3. 总票数-1
    // 4. 模拟延时
    // 要解决一个问题:在threadSaleMethod这一个方法里面,我们就把所有的票卖光!!!
    // 线程锁:所谓线程锁,就是:在修改或者判断共享资源的时候,需要把共享资源加锁,
	//防止别的线程对共享资源进行修改。
    // 使用线程锁的方式:
    // 1. 定义锁,懒加载
    if (_lock == nil) {
        _lock = [[NSLock alloc]init];
    }
    // 注意,全局用锁,不能在线程方法里面定义线程锁
    // 2. 使用共享资源之前,加锁
    // 3. 使用完成共享资源之后,解锁
    // 如果while条件使用了争抢的资源,就需要在外面上锁!
    while (YES) {
        // 加锁,余票>0,则更新UI,余票--,解锁
        [_lock lock];
        if (_tickets > 0) {
            NSString *str = [NSString stringWithFormat:@"当前票数是%d,售票线程是%@",
								_tickets, [[NSThread currentThread]name]];
            // 主线程中执行更新UI界面
            // withObject是参数,waitUntilDone 的意思是:是否等待主线程更新完毕
            [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            _tickets--;
            // 尽早地使用完共享资源,然后解锁,使用完了,我们在这里解锁
            [_lock unlock];
			//模拟休息
            if ([[[NSThread currentThread]name] isEqualToString:@"售票线程-1"]) {
                [NSThread sleepForTimeInterval:0.2];
            } else {
                [NSThread sleepForTimeInterval:1.0];
            }
        } else {
            // 显示谁最后售完票
            NSString *str = [NSString stringWithFormat:@"票已售完%@", [[NSThread currentThread]name]];
            [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            NSLog(@"%@", str);
            // 退出循环
            break;
        }
    }
}
// 响应按钮点击,NSThread按钮方法,售票
- (IBAction)threadSales:(id)sender
{
    // 1. 先设定销售票的数量
    _tickets = 20;
    // 2. 调用售票流程,确认单个程序能够正常运行
    // 是用来测试单个任务是否正常工作的
//    [self threadSaleMethod];
    // 创建线程1,绑定要执行哪个对象的哪个方法
    NSThread *thread1 = [[NSThread alloc]initWithTarget:self 
						selector:@selector(threadSaleMethod) object:nil];
    // 设置线程名,便于跟踪时知道谁在工作
    [thread1 setName:@"售票线程1"];
    // 启动线程
    [thread1 start];
    // 创建线程2,绑定要执行哪个对象的哪个方法
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self
						selector:@selector(threadSaleMethod) object:nil];
	// 设置线程名,便于跟踪时知道谁在工作
    [thread2 setName:@"售票线程2"];
    // 在使用NSThread的时候,千万别忘记start,启动线程
    [thread2 start];
}
@end

H:/0729/02_Thread+NSOperation+Grand Central Dispatcher+售票_ViewController.h
//
//  ViewController.h
//  卖票
//
//  Created by apple on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

// 多行文本提示框
@property (weak, nonatomic) IBOutlet UITextView *infoTextView;

// NSThread售票
- (IBAction)threadSales:(id)sender;

// NSInvocationOperation售票
- (IBAction)invocationSales:(id)sender;

// NSBlockOperation售票
- (IBAction)blockSales:(id)sender;

// GCD售票
- (IBAction)gcdSales:(id)sender;

@end

H:/0729/02_Thread+NSOperation+Grand Central Dispatcher+售票_ViewController.m
</pre><pre name="code" class="objc">//  ViewController.m
//  卖票
//  Created by apple on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import "ViewController.h"
@interface ViewController ()
{
    NSInteger   _tickets;
    // 线程锁,是不能够定义在线程方法里面的
    NSLock      *_lock;
}
@end
@implementation ViewController
// 为了在TextView里面增加文本,更新界面,需要先写一个方法处理。
// 1. 读出当前文本框的内容
// 2. 把要追加的文本附加在当前文本框内容的后面
// 3. 重新为文本框赋值
// 这是负责更新界面的
- (void)appendTextView:(NSString *)text
{
    NSMutableString *str = [NSMutableString stringWithString:_infoTextView.text];
    [str appendFormat:@"\n%@", text];
//    NSLog(@"append text %@", str);
    [_infoTextView setText:str];
    // 用来将文本框滚动到想要的位置
    // 我们现在想要滚动到最后,这个方法的参数是一个NSRange
    // 定义一个NSRange
    NSRange range = NSMakeRange(str.length, 1);
    [_infoTextView scrollRangeToVisible:range];
}
#pragma mark - NSThread售票
// NSThread卖票实际方法,线程跑的方法
- (void)threadSaleMethod
{
    // 1. 判断是否还有票
    // 2. 更新界面,提示当前票数
    // 3. 总票数-1
    // 4. 模拟延时
    // 要解决一个问题:在threadSaleMethod这一个方法里面,我们就把所有的票卖光!!!
    // 线程锁:所谓线程锁,就是:在修改或者判断共享资源的时候,需要把共享资源加锁,防止别的线程对
    // 共享资源进行修改。
    // 使用线程锁的方式:
    // 1. 定义锁,懒加载
    if (_lock == nil) {
        _lock = [[NSLock alloc]init];
    }
    // 不能在线程方法里面定义线程锁
//    NSLock *lock = [[NSLock alloc]init];
    // 2. 使用共享资源之前,加锁
    // 3. 使用完成共享资源之后,解锁
    // 如果while条件使用了争抢的资源,就需要在外面上锁!
    while (YES) {
        // 加锁
        [_lock lock];
        if (_tickets > 0) {
            // 做了一个字符串,显示提示信息
            NSString *str = [NSString stringWithFormat:@"当前票数是%d,售票线程是%@", _tickets, [[NSThread currentThread]name]];
            // 更新界面
            // 在多线程方法里面不能这样使用的
            //        [self appendTextView:@"12"];
            // waitUntilDone 的意思是:是否等待主线程更新完毕
            [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            _tickets--;
            // 使用完了,我们在这里解锁
            [_lock unlock];
            // ??不是不同的线程吗?
            if ([[[NSThread currentThread]name] isEqualToString:@"售票线程-1"]) {
                [NSThread sleepForTimeInterval:0.2];
            } else {
                [NSThread sleepForTimeInterval:0.3];
            }
        } else {
            // 在退出之前需要解锁
            [_lock unlock];
            // 显示一下谁到这里了
            // 做了一个字符串,显示提示信息
            NSString *str = [NSString stringWithFormat:@"票已售完%@", [[NSThread currentThread]name]];
            [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            NSLog(@"%@", str);
            // 退出循环
            break;
        }
    }
}
// NSThread按钮方法,售票
- (IBAction)threadSales:(id)sender
{
    // 1. 先设定销售票的数量
    _tickets = 100;
    // 2. 调用售票流程,确认单个程序能够正常运行
    // 是用来测试单个任务是否正常工作的
//    [self threadSaleMethod];
    // 创建线程1
    NSThread *thread1 = [[NSThread alloc]initWithTarget:self
				selector:@selector(threadSaleMethod) object:nil];
    // 便于跟踪时知道谁在工作
    [thread1 setName:@"售票线程-1"];
    // 启动线程
    [thread1 start];
    // 创建线程2
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self
					selector:@selector(threadSaleMethod) object:nil];
    [thread2 setName:@"售票线程-2"];
    // 在使用NSThread的时候,千万别忘记start,因为另外两给没有
    [thread2 start];
}
#pragma mark - NSOperation售票,由主线程统一修改票数,由主线程统一更新界面UI
// NSOperation售票方法,参数是操作名称
- (void)operationSaleMethod:(NSString *)operationName
{
    // 卖票流程——在这一个方法里面,我们需要把所有的票卖光!!!
    // 1. 是否有票
    // 2. 显示当前票数
    // 3. 票数-1
    // 4. 模拟延时    
    while (YES) {
        // 还有票
        if (_tickets > 0) {
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                NSString *str = [NSString stringWithFormat:@"当前票数:%d,线程名称:%@", _tickets, operationName];
                // 由主线程统一更新界面UI
                [self appendTextView:str];
				// 由主线程统一修改票数
                _tickets--;
            }];
#warning 这里需要模拟延时
            if ([operationName isEqualToString:@"Operation - 1"] || [operationName isEqualToString:@"Block - 1"]) {
                [NSThread sleepForTimeInterval:0.2];
            } else {
                [NSThread sleepForTimeInterval:1.0];
            }
        } else {
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                NSString *str = [NSString stringWithFormat:@"票已售完,线程名称:%@",  operationName];
                // 更新界面UI
                [self appendTextView:str];
            }];
            break;
        }
    }
}
//NSInvocationOperation由主线程统一修改票数,由主线程统一更新界面UI
- (IBAction)invocationSales:(id)sender
{
    _tickets = 20;
    // 1. 定义操作
    // 1.1. 操作需要调用一个方法,NSThread同样如此
    // 1.2. id说明,我们定义的方法是可以接收参数的,能接收一个。
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self 
				selector:@selector(operationSaleMethod:) object:@"Operation - 1"];
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self 
				selector:@selector(operationSaleMethod:) object:@"Operation - 2"];
    // 下面要做什么呢?
    // 2. 定义队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    // 3. 将操作添加到操作队列,它会自动启动
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}
// NSOperationQueue添加操作with Block售票由主线程统一修改票数,由主线程统一更新界面UI
- (IBAction)blockSales:(id)sender
{
    _tickets = 20;
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"Block - 1"];
    }];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"Block - 2"];
    }];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"Block - 3"];
    }];
    [queue setMaxConcurrentOperationCount:2];
}
#pragma mark - GCD售票
// 卖票流程
- (void)gcdSaleMethod:(NSString *)gcdName
{
    // 1. 确认票数
    // 2. 更新界面
    // 3. 票数-1
    // 4. 模拟延时
    while (YES) {
        if (_tickets > 0) {
            // 1. 确定要更新的内容
            // 2. 在主线程队列更新界面,获取调度主队列
            dispatch_async(dispatch_get_main_queue(), ^{
                NSString *str = [NSString stringWithFormat:@"当前票数:%d, 售票线程:%@", _tickets, gcdName];
                [self appendTextView:str];
                
                _tickets--;
            });
            // 模拟延时
            if ([gcdName isEqualToString:@"GCD-1"]) {
                [NSThread sleepForTimeInterval:0.2];
            } else {
                [NSThread sleepForTimeInterval:1.0];
            }
        } else {
			//退出,即任务完成,会通知,群组任务完成
            break;
        }
    }
}
- (IBAction)gcdSales:(id)sender
{
    _tickets = 20;
    
    // 1. 获取全局调度队列
    // 后面的标记永远是0
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 2. 创建调度群组
    dispatch_group_t group = dispatch_group_create();
    // 3. 向调度群组添加异步任务,并指定执行的队列
    // 通过方法的提示,我们知道需要去写一个方法更简单,所以这里先暂停,去写方法。
    dispatch_group_async(group, queue, ^{
        [self gcdSaleMethod:@"GCD-1"];
    });
    dispatch_group_async(group, queue, ^{
        [self gcdSaleMethod:@"GCD-2"];
    });
    // 4. 监听群组所有任务完成的通知,注意第2个参数更新UI,故用主队列,即代码块
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{        
        // 我们要提示用户没有票了!——我们要更新UI
        [self appendTextView:@"票已售完!"];
    });
}
@end


H:/0729/03_类方法创建NSThread+下载图片_ViewController.h
//
//  ViewController.h
//  多线程下载图片
//
//  Created by apple on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

// 图像数组集合
@property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *imageViews;

// NSThread下载方法
- (IBAction)threadDownload:(id)sender;

@end

H:/0729/03_类方法创建NSThread+下载图片_ViewController.m
//  ViewController.m
//  多线程下载图片
//  Created by apple on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import "ViewController.h"
@interface ViewController ()
{
    NSInteger _imageIndex;
}
@end
@implementation ViewController
// 我们是通过网络下载图片的,为了方便重复使用,我们考虑写一个通用的方法,统一调用图像的URL
- (NSURL *)imageURL
{
    // 我们需要随机生成网络图片,服务器上现在共有17张图片,因此我们需要从这17张图片中随机生成URL
    // 生成URL的步骤
    // 1. 定义String
    // 1.1 生成一个1~17之间的数字,去替换图片文件名中的数字
    NSInteger index = arc4random() % 17 + 1;
    NSString *string = [NSString stringWithFormat:@"http://teacher.local/~apple/itcast/images/nat/NatGeo%02d.png", index];
    // 2. URLWithString
    return [NSURL URLWithString:string];
}
#pragma mark - NSThread下载图像
// 更新界面上的图像
- (void)updateImages:(UIImage *)image
{
    // 我们需要知道具体给界面上的哪一个图像去赋值
    // 连线的时候,我们用的是集合,它是有索引的的
    // 这个集合一共有四项
    // 我们用一个成员变量去记录当前更新的图像索引
    // 索引会有一个递增++,但是不能超过4
    // 如果超过4?用取模来处理
    // 这个方法,是在主线程队列上执行的,不涉及到数据安全问题
    _imageIndex = _imageIndex % 4;
    UIImageView *imageView = _imageViews[_imageIndex];
    [imageView setImage:image];
    _imageIndex++;
}
// 串一个图像的URL,根据URL去下载图像
- (void)threadDownloadImage:(NSURL *)imageURL
{
    // 1. 先根据URL获取到NSData;
    // 2. 将NSData转成图像;
    // 因为多线程本身就是并发执行的,因此,我们不用再去考虑同步执行还是异步执行
    NSData *data = [NSData dataWithContentsOfURL:imageURL];
    UIImage *image = [UIImage imageWithData:data];
    // 需要用图像去设置界面上的图像
    // 1. UI需要修改,因此我们需要在主线程上修改
    // 更新图像,我们又需要去写一个方法
    // 还需要去处理,如果image没有获取到,我们就不更新界面了
    if (image != nil) {
        [self performSelectorOnMainThread:@selector(updateImages:)
									withObject:image waitUntilDone:YES];
    }
}
- (IBAction)threadDownload:(id)sender
{
    // 1. 成员方法
    // 1.1. 定义线程
    // 1.2. 启动线程
    // 2. 静态方法
    // 根据静态方法的提示,不再需要启动线程
    // 我们需要一个线程方法,下载图像,同时传入一个参数object
    // 我们一共有4张图片需要下载,那么需要开四个线程
    for (NSInteger i = 0; i < 4; i++) {
        [NSThread detachNewThreadSelector:@selector(threadDownloadImage:)
								toTarget:self withObject:[self imageURL]];
    }
}
@end

H:/0729/04_多线程_ViewController.h
//
//  ViewController.h
//  Tickets
//
//  Created by liufan on 13-7-27.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

// 信息文本视图
@property (weak, nonatomic) IBOutlet UITextView *infoTextView;

// NSThread售票
- (IBAction)threadSales:(id)sender;
// NSBlockOperation售票
- (IBAction)operationSales:(id)sender;
// NSInvocationOperation售票
- (IBAction)invocationSales:(id)sender;
// GCD售票
- (IBAction)gcdSales:(id)sender;

@end

H:/0729/04_多线程_ViewController.m
//
//  ViewController.m
//  Tickets
//
//  Created by liufan on 13-7-27.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
{
    // 剩余票数,共享资源
    int     _tickets;
    // 线程锁,NSThread使用
    NSLock  *_threadLock;
}

@end

@implementation ViewController

// 向文本框追加文本
- (void)appendTextView:(NSString *)text
{
    NSMutableString *str = [NSMutableString stringWithString:[_infoTextView text]];
    [str appendFormat:@"\n%@", text];
    
    NSRange range = NSMakeRange(str.length, 1);
    [_infoTextView setText:str];
    // 滚动至文本框文字末尾
    [_infoTextView scrollRangeToVisible:range];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - NSThread 售票
// NSThread售票方法
- (void)threadSaleMethod
{
    // 懒加载线程锁,线程锁需要是成员变量
    if (_threadLock == nil) {
        _threadLock = [[NSLock alloc]init];
    }
    // 1. 判断是否有剩余票数 > 0
    // 1.1. 显示当前票数
    // 1.2. 卖票
    // 1.3. 模拟卖票休息
    // 2. 显示票已售完
    while (YES) {
        // 加锁
        [_threadLock lock];
                
        if (_tickets > 0) {
            
            NSString *str = [NSString stringWithFormat:@"当前票数:%d,售票线程:%@", _tickets, [[NSThread currentThread]name]];
            // 调用主线程方法更新UI
            [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            
            _tickets--;
            // 解锁
            [_threadLock unlock];
            
            // 模拟休息
            if ([[[NSThread currentThread]name] isEqualToString:@"售票线程-1"]) {
                [NSThread sleepForTimeInterval:1.0f];
            } else {
                [NSThread sleepForTimeInterval:0.1f];
            }
        } else {
            // 解锁
            [_threadLock unlock];
            
            NSString *str = [NSString stringWithFormat:@"票已售完,售票线程:%@", [[NSThread currentThread]name]];
            // 调用主线程方法更新UI
            [self performSelectorOnMainThread:@selector(appendTextView:) withObject:str waitUntilDone:YES];
            
            break;
        }
    }
}

// NSThread售票
- (IBAction)threadSales:(id)sender
{
    // 设置预售票数,共享资源!
    _tickets = 20;
    
    // 创建线程1
    NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSaleMethod) object:nil];
    // 设置线程名称,因为需要通过线程名称跟踪线程执行情况,因此此处不使用线程静态方法
    [thread1 setName:@"售票线程-1"];
    // 启动线程1
    [thread1 start];
    
    // 创建线程2,步骤同上
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadSaleMethod) object:nil];
    [thread2 setName:@"售票线程-2"];
    [thread2 start];
}

#pragma mark - NSOperation 售票
// NSOperation售票方法
- (void)operationSaleMethod:(NSString *)operationName
{
    while (YES) {
        if (_tickets > 0) {

            // 在主线程操作队列更新界面
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                NSString *str = [NSString stringWithFormat:@"当前票数:%d,售票线程:%@", _tickets, operationName];
                [self appendTextView:str];
                _tickets--;
            }];
            // 模拟休息
            if ([operationName isEqualToString:@"售票操作-1"]) {
                [NSThread sleepForTimeInterval:0.1f];
            } else {
                [NSThread sleepForTimeInterval:0.2f];
            }
        } else {
            // 在主线程操作队列更新界面
            [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                NSString *str = [NSString stringWithFormat:@"票已售完,售票线程:%@", operationName];
                [self appendTextView:str];
            }];
            break;
        }
    }
}

// NSBlockOperation售票
- (IBAction)operationSales:(id)sender
{
    // 设置预售票数,共享资源!
    _tickets = 20;

    // 定义操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"售票操作-1"];
    }];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"售票操作-2"];
    }];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"售票操作-3"];
    }];

    // 设置最大线程数量
    [queue setMaxConcurrentOperationCount:2];
}

// NSInvocationOperation售票
- (IBAction)invocationSales:(id)sender
{
    // 设置预售票数,共享资源!
    _tickets = 20;

    // 定义操作1
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票操作-1"];
    // 定义操作2
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票操作-2"];

    // 定义操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

#pragma mark - GCD 售票
// GCD售票方法
- (void)gcdSaleMethod:(NSString *)gcdName
{
    while (YES) {
        if (_tickets > 0) {
            // 在主调度队列更新界面
            dispatch_async(dispatch_get_main_queue(), ^{
                NSString *str = [NSString stringWithFormat:@"当前票数:%d,售票线程:%@", _tickets, gcdName];
                [self appendTextView:str];
                _tickets--;
            });
            // 模拟休息
            if ([gcdName isEqualToString:@"售票GCD-1"]) {
                [NSThread sleepForTimeInterval:0.1f];
            } else {
                [NSThread sleepForTimeInterval:0.2f];
            }
        } else {
            break;
        }
    }
}

// GCD售票
- (IBAction)gcdSales:(id)sender
{
    // 设置预售票数,共享资源!
    _tickets = 20;

    // 1. 获取默认调度优先级的全局调度队列,第二个参数为今后拓展使用,目前始终传入0
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2. 创建任务调度组
    dispatch_group_t group = dispatch_group_create();

    // 3. 调度群组异步任务
    dispatch_group_async(group, queue, ^{
        [self gcdSaleMethod:@"售票GCD-1"];
    });
    dispatch_group_async(group, queue, ^{
        [self gcdSaleMethod:@"售票GCD-2"];
    });
    // 4. 接收群组调度通知
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        [self appendTextView:@"票已售完"];
    });
}

@end

H:/0729/05_多线程下载UIImage_ViewController.h
//
//  ViewController.h
//  多线程UIImage演练
//
//  Created by liufan on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

// 图像视图数组
@property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *imageViews;

// NSThread下载图像
- (IBAction)threadDownload:(id)sender;
// NSOperation下载图像
- (IBAction)operationDownload:(id)sender;
// GCD下载图像
- (IBAction)gcdDownload:(id)sender;

@end

H:/0729/05_多线程下载UIImage_ViewController.m
//
//  ViewController.m
//  多线程UIImage演练
//
//  Created by liufan on 13-7-29.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
{
    // 图像索引
    NSInteger       _imageIndex;
}

@end

@implementation ViewController

// 下载图像的URL,准备了17张图片
- (NSURL *)imageURL
{
    NSInteger index = (arc4random() % 17) + 1;
    NSString *urlString = [NSString stringWithFormat:@"http://localhost/~liufan9/itcast/images/nat/NatGeo%02d.png", index];
    
    return [NSURL URLWithString:urlString];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// 更新图像视图
- (void)updateImageViews:(UIImage *)image
{
    if (_imageIndex > 3) {
        _imageIndex = _imageIndex % 4;
    }
    
    UIImageView *imageView = _imageViews[_imageIndex];
    [imageView setImage:image];
    
    _imageIndex++;
}

#pragma mark - NSThread下载图像
- (void)threadDownloadMethod:(NSURL *)imageURL
{
    // 1. 按照指定URL获取数据
    // 2. 根据网络返回数据建立图像
    NSData *data = [NSData dataWithContentsOfURL:imageURL];
    UIImage *image = [UIImage imageWithData:data];
    
    if (image != nil) {
        // 更新UI
        [self performSelectorOnMainThread:@selector(updateImageViews:) withObject:image waitUntilDone:YES];
    }
}

- (IBAction)threadDownload:(id)sender
{
    // 1. 定义线程调用方法,下载指定URL的图像
    // 2. 定义线程
    // 3. 启动线程
    // 4. 重复定义所有线程任务
    
    // 定义线程任务
    for (NSInteger i = 0; i < 4; i++) {
        [NSThread detachNewThreadSelector:@selector(threadDownloadMethod:) toTarget:self withObject:[self imageURL]];
    }
}

#pragma mark - NSOperation下载图像
- (void)operationDownloadImage:(NSURL *)imageURL
{
    // 1. 按照指定URL获取数据
    // 2. 根据网路返回数据建立图像
    NSData *data = [NSData dataWithContentsOfURL:imageURL];
    UIImage *image = [UIImage imageWithData:data];
    
    if (image != nil) {
        // 更新UI
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            [self updateImageViews:image];
        }];
    }
}

- (IBAction)operationDownload:(id)sender
{
    // 1. 定义操作方法,下载指定URL的图像
    // 2. 定义队列
    // 3. 将操作增加至队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    for (NSInteger i = 0; i < 4; i++) {
        [queue addOperationWithBlock:^{
            [self operationDownloadImage:[self imageURL]];
        }];
    }
}

#pragma mark - GCD下载图像
- (void)gcdDownloadImage:(NSURL *)imageURL
{
    // 1. 按照指定URL获取数据
    // 2. 根据网路返回数据建立图像
    NSData *data = [NSData dataWithContentsOfURL:imageURL];
    UIImage *image = [UIImage imageWithData:data];
    
    if (image != nil) {
        // 更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateImageViews:image];
        });
    }
}

- (IBAction)gcdDownload:(id)sender
{
    // 1. 定义gcd调用方法
    // 1. 获取全局调度队列
    // 2. 创建调度群组
    // 3. 将异步操作添加至群组
    // 4. 获取异步操作执行完成通知
    
    // 注意:如果不需要接收群组通知,可以直接使用dispatch_async方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    for (NSInteger i = 0; i < 4; i++) {
        dispatch_async(queue, ^{
            [self gcdDownloadImage:[self imageURL]];
        });
    }
}
@end

H:/0729/06_多线程_原子属性锁资源_ViewController.h
//
//  ViewController.h
//  Tickets
//
//  Created by liufan on 13-7-27.
//  Copyright (c) 2013年 itcast. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

// 信息文本视图
@property (weak, nonatomic) IBOutlet UITextView *infoTextView;

// NSThread售票
- (IBAction)threadSales:(id)sender;
// NSBlockOperation售票
- (IBAction)operationSales:(id)sender;
// NSInvocationOperation售票
- (IBAction)invocationSales:(id)sender;
// GCD售票
- (IBAction)gcdSales:(id)sender;

@end

H:/0729/06_多线程_原子属性锁资源_ViewController.m
//  ViewController.m
//  Tickets
//  Created by liufan on 13-7-27.
//  Copyright (c) 2013年 itcast. All rights reserved.
#import "ViewController.h"
@interface ViewController ()
{
    // 剩余票数,共享资源
//    int     _tickets;
    // 线程锁,NSThread使用
    NSLock  *_threadLock;
}
// 在iOS中,针对需要抢夺的共享资源,需要将这些资源设置为“原子属性”
// 在程序应用中,直接通过属性的方式调用即可。
@property (assign, atomic) int tickets;
@end
@implementation ViewController
// 向文本框追加文本
- (void)appendTextView:(NSString *)text
{
    NSMutableString *str = [NSMutableString stringWithString:[_infoTextView text]];
    [str appendFormat:@"\n%@", text];
    NSRange range = NSMakeRange(str.length, 1);
    [_infoTextView setText:str];
    // 滚动至文本框文字末尾
    [_infoTextView scrollRangeToVisible:range];
}
#pragma mark - NSThread 售票
// NSThread售票方法
- (void)threadSaleMethod
{
    // 懒加载线程锁,线程锁需要是成员变量
    if (_threadLock == nil) {
        _threadLock = [[NSLock alloc]init];
    }
    // 1. 判断是否有剩余票数 > 0
    // 1.1. 显示当前票数
    // 1.2. 卖票
    // 1.3. 模拟卖票休息
    // 2. 显示票已售完
    while (YES) {
        // 加锁
        [_threadLock lock];
        /**
         说明:
         使用NSThread技术,是自行控制对共享资源的加锁及解锁,无需使用原子属性,可以提高程序的执行效率。
         */
        if (_tickets > 0) {
            // 显示信息内容
            NSString *str = [NSString stringWithFormat:@"当前票数:%d,售票线程:%@",
							_tickets, [[NSThread currentThread]name]];
            // 调用主线程方法更新UI
            [self performSelectorOnMainThread:@selector(appendTextView:)
							withObject:str waitUntilDone:YES];
            _tickets--;
            // 解锁
            [_threadLock unlock];
        } else {
            // 解锁
            [_threadLock unlock];
            NSString *str = [NSString stringWithFormat:@"票已售完,售票线程:%@",
								[[NSThread currentThread]name]];
            // 调用主线程方法更新UI
            [self performSelectorOnMainThread:@selector(appendTextView:)
								withObject:str waitUntilDone:YES];
            break;
        }
        // 模拟休息
        if ([[[NSThread currentThread]name] isEqualToString:@"售票线程-1"]) {
            [NSThread sleepForTimeInterval:1.0f];
        } else {
            [NSThread sleepForTimeInterval:0.1f];
        }
    }
}
// NSThread售票
- (IBAction)threadSales:(id)sender
{
    // 设置预售票数,共享资源!
    _tickets = 20;
    // 创建线程1
    NSThread *thread1 = [[NSThread alloc]initWithTarget:self
					selector:@selector(threadSaleMethod) object:nil];
    // 设置线程名称,因为需要通过线程名称跟踪线程执行情况,因此此处不使用线程静态方法
    [thread1 setName:@"售票线程-1"];
    // 启动线程1
    [thread1 start];
    // 创建线程2,步骤同上
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self
					selector:@selector(threadSaleMethod) object:nil];
    [thread2 setName:@"售票线程-2"];
    [thread2 start];
}
#pragma mark - NSOperation 售票
// NSOperation售票方法
- (void)operationSaleMethod:(NSString *)operationName
{
    while (YES) {
        /**
         注意:
         1. 在NSOperation多线程技术中,读取或者修改抢夺资源的代码,需要使用同步锁!
         2. 需要通过原子属性的方式读取或者修改属性内容。
         */
        @synchronized(self) {
            if (self.tickets > 0) {
                // 在主线程操作队列负责更新界面,其他工作均在当前线程完成
                NSString *str = [NSString stringWithFormat:@"当前票数:%d,售票线程:%@",
								self.tickets, operationName];
                [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                    [self appendTextView:str];
                }];
                self.tickets--;
            } else {
                // 在主线程操作队列更新界面
                [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                    NSString *str = [NSString stringWithFormat:@"票已售完,售票线程:%@", operationName];
                    [self appendTextView:str];
                }];
                break;
            }
        }
        // 模拟休息,需要加在同步锁的外部,这样反而会提高性能
        if ([operationName isEqualToString:@"售票操作-1"]) {
            [NSThread sleepForTimeInterval:0.1f];
        } else {
            [NSThread sleepForTimeInterval:0.5f];
        }
    }
}
// NSBlockOperation售票
- (IBAction)operationSales:(id)sender
{
    /*
     设置预售票数,共享资源!
     说明,此时线程还没有启动,因此可以通过成员变量的方式设置属性数值
     */
    _tickets = 20;
    // 定义操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"售票操作-1"];
    }];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"售票操作-2"];
    }];
    [queue addOperationWithBlock:^{
        [self operationSaleMethod:@"售票操作-3"];
    }];
    // 设置最大线程数量
    [queue setMaxConcurrentOperationCount:2];
}
// NSInvocationOperation售票
- (IBAction)invocationSales:(id)sender
{
    // 设置预售票数,共享资源!
    _tickets = 20;

    // 定义操作1
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]
					initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票操作-1"];
    // 定义操作2
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]
					initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票操作-2"];
    // 定义操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

#pragma mark - GCD 售票
// GCD售票方法
- (void)gcdSaleMethod:(NSString *)gcdName
{
    while (YES) {
        /**
         注意:
         1. 在GCD多线程技术中,读取或者修改抢夺资源的代码,需要使用同步锁!
         2. 需要通过原子属性的方式读取或者修改属性内容。
         */
        @synchronized(self) {
            if (self.tickets > 0) {
                // 在主调度队列更新UI界面,其他操作统统放在当前线程中完成。
                NSString *str = [NSString stringWithFormat:@"当前票数:%d,售票线程:%@", self.tickets, gcdName];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self appendTextView:str];
                });
				// 在主调度队列更新余票
                self.tickets--;
            } else {
                break;
            }
        }
        // 模拟休息
        if ([gcdName isEqualToString:@"售票GCD-1"]) {
            [NSThread sleepForTimeInterval:0.1f];
        } else {
            [NSThread sleepForTimeInterval:0.2f];
        }
    }
}
// GCD售票
- (IBAction)gcdSales:(id)sender
{
    /*
     设置预售票数,共享资源!
     说明,此时线程还没有启动,因此可以通过成员变量的方式设置属性数值
     */
    _tickets = 20;
    // 1. 获取默认调度优先级的全局调度队列,第二个参数为今后拓展使用,目前始终传入0
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 2. 创建任务调度组
    dispatch_group_t group = dispatch_group_create();
    // 3. 调度群组异步任务
    dispatch_group_async(group, queue, ^{
        [self gcdSaleMethod:@"售票GCD-1"];
    });
    dispatch_group_async(group, queue, ^{
        [self gcdSaleMethod:@"售票GCD-2"];
    });
    // 4. 接收群组调度通知
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        [self appendTextView:@"票已售完"];
    });
}
@end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值