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

iOS学习之多线程

2015-07-31 16:48 381 查看
在iOS中提供了4中多线程的方式

以下4中方式:



pthread使用较少

pthread
这是一套通用的多线程api,适用于linux、UNIX、windows 等系统 跨平台技术,可移植 纯c语言

NSThread *current = [NSThread currentThread];

1、创建一个线程id

pthread_t threadId;
pthrad_create(&threadId,NULL,run,NULL);

void *run(void *data)
{
return NULL;
}

NSThread
一个NSThread对象就代表一条线程
使用更加面向对象,可以直接操作线程对象,oc 语言 程序员进行管理 线程的生命周期

主线程相关用法
NSThread *current = [NSThread currentThread];
NSThread *mainThread = [NS]
判断是不是主线程
[NSThread isMainThread];

线程的优先级
NSThread threadPriority
-(BOOL)setThreadPriority:(double);
调度优先级的取值范围是0.0 — 1.0 值越大 优先级越高

1、创建线程
*1
NSThread*thread = [[NSThreadalloc]initWithTarget:self selector:@selector(run:)object:@"aaa"];
//开启线程
[thread
start];

*2 创建后就执行 自动启动
无法对线程进行更详细的设置
[NSThread detachNewThreadSelector:@selector(run:)object:@"aaa"];]

3*//隐式创建
[self
performSelectorInBackground:@selector(run:)withObject:@"aa"];

-(void)run
{

}

线程的生命周期
新建——————》线程对象进入可调度线程池(就绪状态)——————》运行状态(如果cpu调度当前线程)——————》调用sleep方法 当前线程从调度池 ,但是该线程仍然在内存中 —————》当阻塞结束 当前进程进入就绪状态

线程任务执行完毕、异常退出、强制退出 都是 线程进入死亡状态(仍然在内存中)
//阻塞线程
+(void)sleepUntiData:(NSDate:)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
强制停止线程
+(void)exit; 线程进入死亡状态

线程加锁(线程同步)—》多条线程按顺序的执行任务 互斥锁 就是使用了线程同步技术
NSLock *lock = [[NSLock alloc]init];
线程读之前加锁(只能用一把锁)

@synchronized(self){ 加锁
大括号内的代码有线程安全问题代码要进行加锁操作

}大括号结束解锁

线程间通信
两个进行线程间通信的方法
下面方法在主线程中执行settingImage方法
[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];

下面方法是在onThread指定的线程中执行某个方法
[self performSelector:<#(SEL)#> onThread:<#(NSThread *)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#> modes:<#(NSArray
*)#>]

对ui界面的操作要在主线程中执行,例如以下载图片为例可以使用
下面方法的意思是:在主线程中调用imageView 的 setImage:方法将imageView的image设置为 下载好的image
其中waitUntilDone设置为NO为 调用完这个方法会立即返回
waitUntilDone设置为YES 这个方法会在主线程执行完setImage:方法后返回
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];

[self.imageViewperformSelectorOnMainThread:@selector(setImage:)withObject:imagewaitUntilDone:NO];

GCD Grand Central Dispatch 牛逼的中枢调度器

优势:
1、多核的并行运算提出的解决方案
2、会自动利用更多的cpu内核
3、自动关了线程的生命周期(创建线程、调度任务、销毁线程)
4、不需要编写任何线程管理代码 只需要告诉GCD 要执行的任务 GCD 会自动创建队列执行任务

使用:两个步骤
定制任务
添加定制任务到队列中 —》
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循 FIFO 先进先出

//凡是函数名种带有create\copy\new\retain等字眼,都需要在不需要使用这个数据的时候进行release

// GCD的数据类型在ARC环境下不需要再做release
// CF(Core Foundation)的数据类型在ARC环境下还是需要再做release

/**

* 全局并发队列

*/

#define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)

/**

* 主队列

*/
#define main_queue dispatch_get_main_queue()

基本使用:(同步函数和异步函数———决定了要不要开启新的线程)
1、2个用来执行任务的函数
同步:在当前线程中执行任务 不具备开启新线程的能力
dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)

异步:在另一条线程中执行 具备了开启新线程的能力
dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

特殊情况:异步函数的任务添加在主队列中(往主队列中添加一个同步任务), 任务在主线程执行,不会开启新的线程

2、队列
分为两大类(并发和串行————决定了任务的执行方式)
并发队列:可以让多个任务并发同时执行(自动开启多条线程) 在异步函数中执行

串行队列:让任务一个接着一个执行

并发队列:GCD默认已经提供了全局的并发队列供整应用程序使用,不需要手动创建

获取一个全局的默认优先级的并发队列
dispatch_queue_tqueue
=
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,

0);

添加任务到队列中执行任务

1、
用dispatch_async异步函数往并发队列中添加任务—————》同时开启了3个子线程
- (void)asyncGlobalQueue

{
// 获取一个全局的默认优先级的并发队列
dispatch_queue_t

queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

// 2.添加任务到队列中执行

dispatch_async(queue, ^{

NSLog(@"----下载图片1-----%@",
[NSThreadcurrentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----下载图片2-----%@",
[NSThreadcurrentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----下载图片3-----%@",
[NSThreadcurrentThread]);
});
//

总结:同时开启了3个线程
}

2、用dispatch_async异步函数 往串行队列中添加任务————————》只开了一个线程执行任务
- (void)asyncSerialQueue
{

// 1.创建串行队列

dispatch_queue_t
queue =
dispatch_queue_create("com.itheima.queue",NULL);

// 2.添加任务到队列中执行

dispatch_async(queue, ^{

NSLog(@"----下载图片1-----%@",
[NSThreadcurrentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----下载图片2-----%@",
[NSThreadcurrentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----下载图片3-----%@",
[NSThreadcurrentThread]);

});
//总结:只开1个线程执行任务
}

3、用dispatch_sync同步函数往并发队列中添加任务—————》不会开启新的线程,并发队列失去了并发的功能
- (void)syncGlobalQueue

{

// 1.获得全局的并发队列

dispatch_queue_t
queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

// 2.添加任务到队列中执行

dispatch_sync(queue, ^{

NSLog(@"----下载图片1-----%@",
[NSThreadcurrentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"----下载图片2-----%@",
[NSThreadcurrentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"----下载图片3-----%@",
[NSThreadcurrentThread]);

});

//
总结:不会开启新的线程,并发队列失去了并发的功能
}

4、用dispatch_sync同步函数往串行列中添加任务—————》不会开启新的线程
- (void)syncSerialQueue

{
// 1.创建串行队列(串行队列)
dispatch_queue_t

queue =
dispatch_queue_create("com.itheima.queue",NULL);

// 2.添加任务到队列中执行

dispatch_sync(queue, ^{

NSLog(@"----下载图片1-----%@",
[NSThreadcurrentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"----下载图片2-----%@",
[NSThreadcurrentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"----下载图片3-----%@",
[NSThreadcurrentThread]);

});

// 3.释放资源

// dispatch_release(queue); // MRC(非ARC)

//
总结:不会开启新的线程
}

5、用dispatch_sync同步函数,在主线程中往主队列中添加任务

: 任务无法往下执行———————>产生死锁(死循环)
- (void)syncMainQueue

{

// 1.获得主队列

dispatch_queue_t
queue =
dispatch_get_main_queue();

// 2.添加任务到队列中执行
(queue, ^{
NSLog(@"----下载图片1-----%@",
[NSThreadcurrentThread]);

});

// dispatch_sync(queue, ^{

// NSLog(@"----下载图片2-----%@",
[NSThread currentThread]);

// });

// dispatch_sync(queue, ^{

// NSLog(@"----下载图片3-----%@",
[NSThread currentThread]);

// });

}

6、
使用dispatch_async异步函数,在主线程中往主队列中添加任务
特殊情况:异步函数的任务添加在主队列中(往主队列中添加一个同步任务), 任务在主线程执行,不会开启新的线程

- (void)asyncMainQueue
{

// 1.获得主队列(串行队列)
dispatch_queue_t

queue =
dispatch_get_main_queue();

// 2.添加任务到队列中执行

dispatch_async(queue, ^{

NSLog(@"----下载图片1-----%@",
[NSThreadcurrentThread]);

});
}

同步函数(sync)
并发队列:不会开启线程
串行队列:不会开启线程
异步函数(async)
并发队列:开启多个线程
串行队列:开启一条线程

使用GCD进行异步图片下载
1、获取全局并发队列

dispatch_queue_tqueue =

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
2、调用异步(同步)函数下载图片
dispatch_async(queue, ^{//开启异步线程下载图片
NSLog(@"当前线程%@",[NSThreadcurrentThread]);

NSURL
*url = [NSURLURLWithString:@"http://www.res.meizu.com/resources/www_image/weixin.jpg"];

NSData
*data = [NSDatadataWithContentsOfURL:url];

UIImage
*image = [UIImageimageWithData:data];

3、回到主队列刷新图片
dispatch_async(dispatch_get_main_queue(),
^{

self.imageView.image=
image;

});
});

GCD的其他用法

1、延迟执行
*1
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];

*2 利用GCD 进行延迟执行(推荐)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// /**

// * 2秒钟后
在队列queue中执行block中的任务

// */

// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{

//
// });

2、一次性代码 使某一段代码在整个程序运行过程中只执行一次,用于创建单 dispatch_once
static id _instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc]init];
});

3、队列组 当程序需要执行完多个耗时操作后 在执行某个其他操作的时候使用
如:下载两张图片 下载完成后进行拼接显示在imageView上
*1、 创建一个组
dispatch_group_t

group =
dispatch_group_create();
*2、开启一个任务下载图片1
__block

UIImage
*image1 =
nil;

//开启一个任务下载图片1

dispatch_group_async(group,

global_queue, ^{

image1 = [self

imageWithUrl:@"1"];
});
*3 开启一个任务下载图片2

__block
UIImage
*image2 =
nil;

//开启一个任务下载图片2

dispatch_group_async(group,

global_queue, ^{
image2 = [self

imageWithUrl:@"2"];
});
4* 等待group中的所有任务都执行完毕后 在执行其他操作

dispatch_group_notify(group,

main_queue, ^{
//合并图
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
[image1 drawInRect:CGRectMake(0, 0, 100, 100)];
[image2 drawInRect:CGRectMake(100, 0, 100, 100)];
UIImageView *imageView = nil;
imageView.image = UIGraphicsGetImageFromCurrentImageContext();

});

NSOperation

使用NSOperation 的子类 创建任务 NSInvocationOperation 、 NSBlockOperation

1、封装任务
NSInvocationOperation

*opration1 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(download)object:nil];

NSInvocationOperation

*opration2 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(run)object:nil];

NSBlockOperation

*blockOperation = [NSBlockOperationblockOperationWithBlock:^{

NSLog(@"%@——1----",[NSThreadcurrentThread]);
}];

[blockOperationaddExecutionBlock:^{

NSLog(@"%@—2-------",[NSThreadcurrentThread]);
}];

以上有三个操作(opration1、opration2、blockOperation)
4个任务(download、run、%@——1——、%@——2----)

2、创建队列

NSOperationQueue*queue
= [[NSOperationQueuealloc]init];

//最大并发数 同一时间只能做
2件事 控制开线程的个数 queue.maxConcurrentOperationCount=

2;//2---3

//任务在队列中的优先级
/*

NSOperationQueuePriorityVeryLow = -8L,

NSOperationQueuePriorityLow = -4L,

NSOperationQueuePriorityNormal = 0,

NSOperationQueuePriorityHigh = 4,

NSOperationQueuePriorityVeryHigh = 8
*/
opration1.queuePriority=

NSOperationQueuePriorityNormal;

操作依赖
//操作依赖 在添加到队列之前
----可以再不同队列间的operation之间添加依赖 不能相互依赖
//opration1依赖
opration2
当opration2执行完毕后才能 执行opration1
[opration1addDependency:opration2];

//操作的监听

//当blockOperation中的操作完成后在当前线程中执行下面操作

blockOperation.completionBlock=
^{

};

//添加到队列中---------》异步执行 多个任务一定是并行操作[queueaddOperation:opration1];
[queueaddOperation:opration2];
[queueaddOperation:blockOperation];

自定义NSOperation

创建属性
/**

* 下载图片的队列

*/

@property(nonatomic,strong)NSOperationQueue

*queue;

/** key:url value:operation对象*/
@property(nonatomic,strong)NSMutableDictionary
*operations;
/** key:url value:image对象*/
@property(nonatomic,strong)NSMutableDictionary

*images;

懒加载
- (NSArray*)apps

{

if
(!_apps) {

NSArray
*dictArray = [NSArrayarrayWithContentsOfFile:[[NSBundlemainBundle]pathForResource:@"apps.plist"ofType:nil]];

NSMutableArray
*appArray = [NSMutableArrayarray];

for
(NSDictionary*dict

in
dictArray) {

HMApp
*app = [HMAppappWithDict:dict];

[appArray
addObject:app];

}

_apps
= appArray;

}

return
_apps;

}

- (NSOperationQueue*)queue

{

if
(!_queue) {

_queue
= [[NSOperationQueuealloc]init];

_queue.maxConcurrentOperationCount=

3;//

最大并发数
== 3

}

return
_queue;

}

- (NSMutableDictionary*)operations

{

if
(!_operations) {

_operations
= [NSMutableDictionarydictionary];

}

return
_operations;

}

- (NSMutableDictionary*)images

{

if
(!_images) {

_images
= [NSMutableDictionarydictionary];

}

return
_images;
}

cell中下载图片的代码
//显示图片

//
保证一个url对应一个HMDownloadOperation

//
保证一个url对应UIImage对象

UIImage
*image =
self.images[app.icon];

if
(image) {
// 内存缓存中有图片

cell.imageView.image=
image;

}
else
{
// 内存缓存中没有图片,得下载

// cell.imageView.image = [UIImage imageNamed:@"57437179_42489b0"];

HMDownloadOperation
*operation =
self.operations[app.icon];

if
(operation) {
//
正在下载

// ...
暂时不需要做其他事

}
else
{
//
没有正在下载

//
创建操作

operation = [[HMDownloadOperationalloc]init];

operation.url=
app.icon;

operation.delegate=

self;

operation.indexPath=
indexPath;

[self.queueaddOperation:operation];//

异步下载

self.operations[app.icon]
= operation;

}
}

#pragma mark - HMDownloadOperationDelegate

- (void)downloadOperation:(HMDownloadOperation*)operation
didFinishDownload:(UIImage*)image

{

// 1.移除执行完毕的操作

[self.operationsremoveObjectForKey:operation.url];

if
(image) {

// 2.将图片放到缓存中(images)

self.images[operation.url]
= image;

// 3.刷新表格

[self.tableViewreloadRowsAtIndexPaths:@[operation.indexPath]withRowAnimation:UITableViewRowAnimationNone];

// 3.将图片写入沙盒

// NSData *data = UIImagePNGRepresentation(image);

// [data writeToFile:@"" atomically:<#(BOOL)#>];

}

}

tableView滚动的时候的操作
- (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView

{

//
开始拖拽

//
暂停队列

[self.queuesetSuspended:YES];

}

- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inoutCGPoint

*)targetContentOffset

{

[self.queuesetSuspended:NO];
}

自定义NSOperation的代码
HMDownloadOperation.h
@classHMDownloadOperation;

@protocolHMDownloadOperationDelegate
<NSObject>

@optional

- (void)downloadOperation:(HMDownloadOperation*)operation
didFinishDownload:(UIImage*)image;

@end

@interfaceHMDownloadOperation
:
NSOperation

@property(nonatomic,copy)NSString

*url;

@property(nonatomic,strong)NSIndexPath

*indexPath;

@property(nonatomic,weak)id<HMDownloadOperationDelegate>
delegate;
@end

HMDownloadOperation.m
/**

* 在main方法中实现具体操作

*/

- (void)main

{

@autoreleasepool
{

if
(self.isCancelled)return;

NSURL
*downloadUrl = [NSURLURLWithString:self.url];

NSData
*data = [NSDatadataWithContentsOfURL:downloadUrl];//

这行会比较耗时

if
(self.isCancelled)return;

UIImage
*image = [UIImageimageWithData:data];

if
(self.isCancelled)return;

if
([self.delegaterespondsToSelector:@selector(downloadOperation:didFinishDownload:)])
{

dispatch_async(dispatch_get_main_queue(),
^{
//
回到主线程,传递图片数据给代理对象

[self.delegatedownloadOperation:selfdidFinishDownload:image];

});

}

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