您的位置:首页 > 移动开发 > IOS开发

iOS的三种多线程技术:

2013-10-07 21:54 302 查看
1. NSThread
1> 类方法 detachNewThreadSelector
直接启动线程,调用选择器方法

2> 成员方法 initWithTarget
需要使用start方法,才能启动实例化出来的线程

优点:简单
缺点:
1 控制线程的生命周期比较困难
2 控制并发线程数
3 先后顺序困难
例如:下载图片(后台线程) -> 滤镜美化(后台线程) -> 更新UI(主线程)

2. NSOperation
1> NSInvocationOperation
2> NSBlockOperation

定义完Operation之后,将操作添加到NSOperationQueue即可启动线程,执行任务

使用:
1> setMaxConcurrentOperationCount 可以控制同时并发的线程数量
2> addDependency 可以指定线程之间的依赖关系,从而达到控制线程执行顺序的目的

// Dependency依赖
// 提示:依赖关系可以多重依赖
// 注意:不要建立循环依赖
[op2 addDependency:op1];
[op3 addDependency:op2];


提示:
要更新UI,需要使用[NSOperationQueue mainQueue]addOperationWithBlock:
在主操作队列中更新界面

3. GCD
1) 全局global队列
方法:dispatch_get_global_queue(获取全局队列)
优先级:DISPATCH_QUEUE_PRIORITY_DEFAULT
所有添加到主队列中的任务都是并发执行的

2) 串行队列
方法:dispatch_queue_create(创建串行队列,串行队列不能够获取)
所有添加到串行队列中的任务都是顺序执行的

3) 主队列
主线程队列
方法:dispatch_get_main_queue(获取主队列)
所有添加到主队列中的任务都是在主线程中执行的

在gcd中,同步还是异步取决于任务执行所在的队列,更方法名没有关系

获取队列的方法:

全局队列(可能会开启多条线程)

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

串行队列(只可能会开启一条线程)

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

主队列

dispatch_get_main_queue();

多线程使用注意事项:
线程使用不是无节制的
iOS中的主线程的堆栈大小是1M
从第二个线程开始都是512KB
这些数值不能通过编译器开关或线程API函数更改
只有主线程有直接修改UI的能力

static Ticket *SharedInstance;

@implementation Ticket

/**
实现单例模型需要做三件事情

1. 使用全局静态变量记录住第一个被实例化的对象
static Ticket *SharedInstance

2. 重写allocWithZone方法,并使用dispatch_once_t,从而保证在多线程情况下,
同样只能实例化一个对象副本

3. 建立一个以shared开头的类方法实例化单例对象,便于其他类调用,同时不容易引起歧义
同样用dispatch_once_t确保只有一个副本被建立

关于被抢夺资源使用的注意事项

在多线程应用中,所有被抢夺资源的属性需要设置为原子属性
系统会在多线程抢夺时,保证该属性有且仅有一个线程能够访问

注意:使用atomic属性,会降低系统性能,在开发多线程应用时,尽量不要资源
另外,atomic属性,必须与@synchronized(同步锁)一起使用

*/

// 使用内存地址实例化对象,所有实例化方法,最终都会调用此方法
// 要实例化出来唯一的对象,需要一个变量记录住第一个实例化出来的对象
+ (id)allocWithZone:(NSZone *)zone
{
// 解决多线程中,同样只能实例化出一个对象副本
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SharedInstance = [super allocWithZone:zone];
});

return SharedInstance;
}

// 建立一个单例对象,便于其他类调用
+ (Ticket *)sharedTicket
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SharedInstance = [[Ticket alloc]init];
});

return SharedInstance;
}

@end


@property (weak, nonatomic) UITextView *textView;

@property (strong, nonatomic) NSOperationQueue *queue;

@end

@implementation MainViewController

#pragma mark 追加多行文本框内容
- (void)appendContent:(NSString *)text
{
// 1. 取出textView中的当前文本内容
NSMutableString *str = [NSMutableString stringWithString:self.textView.text];

// 2. 将text追加至textView内容的末尾
[str appendFormat:@"%@\n", text];

// 3. 使用追加后的文本,替换textView中的内容
[self.textView setText:str];

// 4. 将textView滚动至视图底部,保证能够及时看到新追加的内容
NSRange range = NSMakeRange(str.length - 1, 1);
[self.textView scrollRangeToVisible:range];
}

- (void)viewDidLoad
{
[super viewDidLoad];

// 建立多行文本框
UITextView *textView = [[UITextView alloc]initWithFrame:self.view.bounds];
// 不能编辑
[textView setEditable:NO];
[self.view addSubview:textView];
self.textView = textView;

// 预设可以卖30张票
[Ticket sharedTicket].tickets = 30;

// 实例化操作队列
self.queue = [[NSOperationQueue alloc]init];

// 开始卖票
[self operationSales];
}
#pragma mark - NSThread卖票
- (void)threadSaleTicketWithName:(NSString *)name
{
// 使用NSThread时,线程调用的方法千万要使用@autoreleasepool
@autoreleasepool {
while (YES) {
@synchronized(self) {
if ([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;

NSString *str = [NSString stringWithFormat:@"剩余票数 %d 线程名称 %@", [Ticket sharedTicket].tickets, name];

// 更新UI
[self performSelectorOnMainThread:@selector(appendContent:) withObject:str waitUntilDone:YES];
} else {
break;
}
}

// 模拟休息
if ([name isEqualToString:@"thread-1"]) {
[NSThread sleepForTimeInterval:1.0f];
} else {
[NSThread sleepForTimeInterval:0.1f];
}
}
}
}

- (void)threadSales
{
[NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-1"];
[NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-2"];
}

#pragma mark - NSOperation卖票
- (void)operationSaleTicketWithName:(NSString *)name
{
while (YES) {
// 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分
@synchronized(self) {
// 判断是否还有票
if ([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;

// 提示,涉及到被抢夺资源的内容定义方面的操作,千万不要跨线程去处理
NSString *str = [NSString stringWithFormat:@"剩余票数 %d 线程名称 %@", [Ticket sharedTicket].tickets, name];

// 更新UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
[self appendContent:str];
}];
} else {
NSLog(@"卖票完成 %@ %@", name, [NSThread currentThread]);
break;
}
}
// 模拟卖票休息
if ([name isEqualToString:@"op-1"]) {
[NSThread sleepForTimeInterval:0.6f];
} else {
[NSThread sleepForTimeInterval:0.4f];
}
}
}

- (void)operationSales
{
// 提示,operation中没有群组任务完成通知功能
// 两个线程卖票
[self.queue setMaxConcurrentOperationCount:2];

[self.queue addOperationWithBlock:^{
[self operationSaleTicketWithName:@"op-1"];
}];
[self.queue addOperationWithBlock:^{
[self operationSaleTicketWithName:@"op-2"];
}];
[self.queue addOperationWithBlock:^{
[self operationSaleTicketWithName:@"op-3"];
}];
}

#pragma mark - GCD卖票
- (void)gcdSaleTicketWithName:(NSString *)name
{
while (YES) {
// 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分
@synchronized(self) {
if ([Ticket sharedTicket].tickets > 0) {

[Ticket sharedTicket].tickets--;

// 提示内容
NSString *str = [NSString stringWithFormat:@"剩余票数 %d, 线程名称 %@", [Ticket sharedTicket].tickets, name];

// 更新界面
dispatch_sync(dispatch_get_main_queue(), ^{
[self appendContent:str];
});
} else {
break;
}
}

// 模拟线程休眠
if ([name isEqualToString:@"gcd-1"]) {
[NSThread sleepForTimeInterval:1.0f];
} else {
[NSThread sleepForTimeInterval:0.2f];
}
}
}

- (void)gcdSales
{
// 1) 创建全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 2) 创建三个个异步任务分别卖票
//    dispatch_async(queue, ^{
//        [self gcdSaleTicketWithName:@"gcd-1"];
//    });
//    dispatch_async(queue, ^{
//        [self gcdSaleTicketWithName:@"gcd-2"];
//    });
//    dispatch_async(queue, ^{
//        [self gcdSaleTicketWithName:@"gcd-3"];
//    });

// 3. GCD中可以将一组相关联的操作,定义到一个群组中
// 定义到群组中之后,当所有线程完成时,可以获得通知
// 1) 定义群组
dispatch_group_t group = dispatch_group_create();

// 2) 定义群组的异步任务
dispatch_group_async(group, queue, ^{
[self gcdSaleTicketWithName:@"gcd-1"];
});
dispatch_group_async(group, queue, ^{
[self gcdSaleTicketWithName:@"gcd-2"];
});

// 3) 群组任务完成通知
dispatch_group_notify(group, queue, ^{
NSLog(@"卖完了");
});
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: