H:/0729/01_对象方法创建NSThread+加锁+售票_ViewController.h
H:/0729/01_对象方法创建NSThread+加锁+售票_ViewController.m
H:/0729/02_Thread+NSOperation+Grand Central Dispatcher+售票_ViewController.h
H:/0729/02_Thread+NSOperation+Grand Central Dispatcher+售票_ViewController.m
H:/0729/03_类方法创建NSThread+下载图片_ViewController.h
H:/0729/03_类方法创建NSThread+下载图片_ViewController.m
H:/0729/04_多线程_ViewController.h
H:/0729/04_多线程_ViewController.m
H:/0729/05_多线程下载UIImage_ViewController.h
H:/0729/05_多线程下载UIImage_ViewController.m
H:/0729/06_多线程_原子属性锁资源_ViewController.h
H:/0729/06_多线程_原子属性锁资源_ViewController.m
// 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