iOS中多线程的简单应用
NSThread:轻量级的多线程开发,需要程序员自己去管理线程的生命周期
1、创建、启动线程
(1)先创建线程,再启动线程
#pragma mark -- 先创建线程,在启动线程
-(void)run//新线程需要调用的方法,里面是需要执行的任务
{
NSLog(@"%@",[NSThread currentThread]);
}
- (void)viewDidLoad {
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];//1、创建线程
[thread start];//2、启动线程
}
(2)创建线程后自动启动线程
-(void)run//新线程需要调用的方法,里面是需要执行的任务
{
NSLog(@"%@",[NSThread currentThread]);
}
- (void)viewDidLoad {
[super viewDidLoad];
//1、创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
}
(3)隐式的创建并启动线程
-(void)run//新线程需要调用的方法,里面是需要执行的任务
{
NSLog(@"%@",[NSThread currentThread]);
}
- (void)viewDidLoad {
[super viewDidLoad];
//1、创建线程后自动启动线程
[self performSelectorInBackground:@selector(run) withObject:nil];//隐式创建并启动线程
}
2、线程相关的用法:
//获得主线程
+(NSThread *)mainThread;
//判断是否是主线程(对象方法)
-(BOOL)isMainThread;
//获得当前线程
NSThread *current = [NSThread currentThread];
//线程的名字—-set方法
-(void)setName:(NSString *)n;
//线程的名字—get方法
-(void)getName:(NSString *)n;
3、线程之间的通信
一般情况下会在子进程中进行耗时操作,例如去下载图片等,操作结束之后回到主线程去刷新UI,
4、在加载过程:
1)请求数据
2)将数据显示到UI控件,只能在主线程中更新UI
5、多线程下载图片:
方法一、使用对象方法:
1)创建一个线程,第一个参数是请求的操作,第二个参数是操作方法的参数
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage) object:nil];
2)启动一个线程,注意启动一个线程并非一定立即执行,而是处于就绪状态,当系统调度时才真正的执行
[thread start];
方法二:使用类方法
[NSThread detachNewThreadSelector@selector(loadImage) toTarget:self withObject:nil];
注意:利用Thread 中的currentThread方法可以取得当前操作线程,其中包含了线程的名称以及编号number,主线程的编号永远是1.
6、关于并发问题:
1)利用多线程下载图片:
For ( int I = 0; i < 5;i ++ )
{
NSThread *thread =[ [NSTread alloc]initWithTarget:self selectoe:@selector(loadImage:) object”[NSNumber numberWIthInt:i]];
[thread.name = [NSString stringWithFormat:@“myThread%I”,I];//用来设置线程的名称
}
GCD:基于C语言开发的一套多线程开发机制,对于多核运算更加有效。有个类似于NSOperationQueue的队列,GCD统一管理整个队列中的任务,但是GCD中的队列分为并行队列和串行队列两类:
并行队列:只有一个线程,加入到队列中的操作按照添加顺序依次进行。
并发队列:有多个线程,操作进来之后他会将这些队列安排在可用的处理器上,同时保证先进来的任务优先处理。
代码展示:
#pragma mark 加载图片
-(void)loadImage:(NSNumber *)index{
//如果在串行队列中会发现当前线程打印变化完全一样,因为他们在一个线程中
NSLog(@"thread is :%@",[NSThread currentThread]);
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 serialQueue=dispatch_queue_create("myThreadQueue1", DISPATCH_QUEUE_SERIAL);//注意queue对象不是指针类型
//创建多个线程用于填充图片
for (int i=0; i<count; ++i) {
//异步执行队列任务
dispatch_async(serialQueue, ^{
[self loadImage:[NSNumber numberWithInt:i]];
});
}
//非ARC环境请释放
// dispatch_release(seriQueue);
}
2、并发队列:
-(void)loadImageWithMultiThread{
int count=ROW_COUNT*COLUMN_COUNT;
/*取得全局队列
第一个参数:线程优先级
第二个参数:标记参数,目前没有用,一般传入0
*/
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]];
});
}
}
-
在GDC中一个操作是多线程执行还是单线程执行取决于当前队列类型和执行方法,只有队列类型为并行队列并且使用异步方法执行时才能在多个线程中执行。
-
串行队列可以按顺序执行,并行队列的异步方法无法确定执行顺序。
-
UI界面的更新最好采用同步方法,其他操作采用异步方法。
3、其他任务的执行方法:
GCD执行任务的方法并非只有简单的同步调用方法和异步调用方法,还有其他一些常用方法:
-
dispatch_apply():重复执行某个任务,但是注意这个方法没有办法异步执行(为了不阻塞线程可以使用dispatch_async()包装一下再执行)。
-
dispatch_once():单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行(单例模式中常用此方法)。
-
dispatch_time():延迟一定的时间后执行。
-
dispatch_barrier_async():使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。(利用这个方法可以控制执行顺序,例如前面先加载最后一张图片的需求就可以先使用这个方法将最后一张图片加载的操作添加到队列,然后调用dispatch_async()添加其他图片加载任务)
-
dispatch_group_async():实现对任务分组管理,如果一组任务全部完成可以通过dispatch_group_notify()方法获得完成通知(需要定义dispatch_group_t作为分组标识)。
4、线程同步
为了防止资源抢夺,给资源进行加锁机制。ios中有两种方法:
1)使用NSLock同步锁,
2)使用@synchronized代码块
两种方式的实现机制是相同的,只是代码块使用比较简单。