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

iOS 多线程之GCD

2016-01-15 20:44 507 查看
1、一个应用是如何在设备上运行的?

编译器会将我们写的代码转化为二进制代码,这些二进制代码就是操作CPU的命令列。一个App包安装到iPhone上,实质就是汇集了这个App要执行的所有CPU命令列和数据的总和安装到iPhone上了。

App启动之后,iOS会根据用户的操作,首先将包含在应用程序中的CPU命令列配置到内存中,接着CPU从应用程序指定的地址开始,一个一个的执行CPU的命令列。在Xcode中下一个断点,进行单步调试,会看到下面的二进制代码。CPU执行命令列总是从pushq开始,执行到popq,整个命令列就结束。

0x10749daea <+0>:    pushq  %rbp
0x10749daeb <+1>:    movq   %rsp, %rbp
0x10749daf7 <+13>:   subq   $0x1f8, %rsp
0x10749dafe <+20>:   movq   %rdi, %rbx
0x10749db01 <+23>:   movq   0xb9ef38(%rip), %r14      ; (void UIViewController._view
0x10749db16 <+44>:   cmpq   $0x0, (%rbx,%r13)
0x10749db1b <+49>:   jne    0x10749e2af               ; <+1989>
0x10749db21 <+55>:   movq   0xb24058(%rip), %rsi      ; "_isDeallocating"
0x10749db28 <+62>:   movq   %rbx, %rdi
0x10749db2b <+65>:   callq  *0xb9f67f(%rip)           ; (void *)0x00000001069e6800: objc_msgSend
0x10749db31 <+71>:   testb  %al, %al
0x10749db33 <+73>:   je     0x10749db60               ; <+118>
0x10749db35 <+75>:   movb   0xb99e07(%rip), %al       ;
0x10749db4a <+96>:   xorl   %eax, %eax
0x10749db4c <+98>:   movq   %rbx, %rsi
0x10749db4f <+101>:  callq  0x107dafa74               ; symbol stub for: NSLog
0x10749db54 <+106>:  movb   $0x1, 0xb99de7(%rip)      ;
0x10749dcdb <+497>:  movq   %rbx, %rdi
0x10749e2c5 <+2011>: popq   %rbp


2、什么是iOS的线程?

由于一个CPU一次只能执行一个命令,不能执行某处分开并列的两个命令,因此通过CPU执行的CPU执行列可看成是一条没有分叉的路径,CPU在执行的过程中不会产生分歧。因此,一个完整的没有分叉的CPU执行命令列就是一个线程。

3、什么是多线程?

由线程的本质可以知道,多条完整的没有分歧的CPU执行命令列就是多线程。

4、Apple中多线程的实现

Apple引入了一种叫做”上下文切换“的方式来实现多线程。

OSX 和 iOS的核心XNU内核在发生系统操作事件时,会切换执行路径。例如,从路径A切换到路径B,切换时,系统会将当前路径A的状态,即路径A对应的CPU的寄存器的信息保存到各自路径专用的内存块中;然后,从B路径的路径专用内存块中复原CPU寄存器信息,继续执行B路径的CPU命令列。由于使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此,看起来就像是一个CPU核心能够并列的执行多个线程一样。

5、 GCD是什么?

GCD是iOS中异步执行任务的技术之一,iOS开发中常用的系统级的多线程管理手段。GCD是纯C语言的API,执行速度很快,相比较Objective-C对象 operation,也更轻量级,开销也更小。

6、GCD常用API

1、队列
全局队列,一个并行的队列
dispatch_get_global_queue
主队列,主线程中的唯一队列,一个串行队列
dispatch_get_main_queue

2、自定义队列
串行队列
dispatch_queue_create("serialqueue",DISPATCH_QUEUE_SERIAL);
并行队列
dispatch_queue_create("serialqueue",DISPATCH_QUEUE_CONCURRENT);

3、同步异步线程创建

同步线程
dispatch_sync(..., ^(block))
异步线程
dispatch_asyns(..., ^(block))

4、给多个并发线程添加障碍物 barrier 解决多线程并发读写同一个资源发生死锁
dispatch_barrier_async(queue, ^{});

5、快速迭代,避免线程爆炸应该使用 dispatch_apply
dispatch_apply(count,queue, ^(size_t i){});

6、dispatch groups
是专门用来监视多个异步任务,使用dispatch_group_notify异步执行多个任务的汇总。
使用dispatch_group_wait容易造成线程阻塞,不建议使用。

创建一个group
dispatch_group_t = dispatch_group_create();
dispatch_group_async(group, queue, ^{});

汇总通知,这个是异步的
dispatch_group_notify(gruoup, queue), ^{});

同步线程,可能会发生线程阻塞
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

7、dispatch block
队列的执行任务都是block的方式

创建一个block
dispatch_block_t block = dispatch_block_create(0, ^{....});
dispatch_async(queue, block);

//    QOS Way
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{...});
dispatch_async(queue, qosBlock);

8、Dispatch IO文件的操作 ,多个线程去读文件的切片数据,对于大的数据文件这样就会比单线程快好多。

dispatch_async(queue,^{ “ read 0-99 bytes ”});
dispatch_async(queue,^{ “ read 100-199 bytes ”});
dispatch_async(queue,^{ “ read 200-299 bytes ”});

9、Dispatch Semaphore 信号量 保证线程同步的方法。使用dispatch_semaphore_signal加1 dispatch_semaphore_wait减1,为0时等待的设置方式来达到线程同步的目的和同步锁一样能够解决资源抢占的问题。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_semaphore_signal(semaphore);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

10、Dispatch Source
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
dispatch_resume(source);
dispatch_source_merge_data(source, 1);

11、锁
NSRecursiveLock: 递归锁
NSDistributeLock:分布锁
NSConditionLock: 条件所
OSSpinLock: 自旋锁
pthread_mutex_t: 同步锁基于C语言,底层api性能高,使用方法和其它的类似
@synchroinized: 更加简单

12、dispatch_set_context 和 dispatch_get_context
13、dispatch_suspend 和 dispatch_resume  挂起和恢复队列


补充 :NSTimer在主线程的runloop里会在runloop切换其它模式时停止,这时就需要手动在子线程开启一个模式为NSRunLoopCommonModes的runloop,如果不想开启一个新的runloop可以用不跟runloop关联的dispatch source timer,如下。

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
dispatch_source_set_event_handler(source, ^(){
NSLog(@"Time flies.");
});
dispatch_time_t start
dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC,100ull * NSEC_PER_MSEC);
self.source = source;
dispatch_resume(self.source);


简单的使用GCD相关API的Demo

- (void)moveFileWithApply{
NSString *fromPath = @"/Users/HW/Desktop/speedTestApp";
NSString *toPath = @"/Users/HW/Desktop/book";

NSFileManager *manager= [NSFileManager defaultManager];
NSArray *subPaths = [manager subpathsOfDirectoryAtPath:fromPath error:nil];

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSInteger count = subPaths.count;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_apply(count, queue, ^(size_t index) {
NSString *subpath = subPaths[index];
NSLog(@"%@ == ",subpath);
NSString *fromFullPath = [fromPath stringByAppendingPathComponent:subpath];
NSLog(@"fileFullPath == %@# == ",fromFullPath);
NSString *toFullFilePath = [toPath stringByAppendingPathComponent:subpath];

[manager moveItemAtPath:fromPath toPath:toFullFilePath error:nil];
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"所有的文件都已经移动完毕,回到主线程");
});
});
}

- (void)dipatchCombie{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//   异步下载图片一
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:data];
});
//    异步下载图片2
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"http://pic28.nipic.com/20130402/9252150_190139450381_2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image2 = [UIImage imageWithData:data];
});
//    拦截通知,回到主线程去合成图片
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//绘制图片
UIGraphicsBeginImageContext(CGSizeMake(240, 240));
[image1 drawInRect:CGRectMake(0, 0, 240, 120)];
[image2 drawInRect:CGRectMake(0, 120, 240, 120)];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(20, 100, 240, 240)];
iv.image = image;
imageView = iv;
[self.view addSubview:iv];
});
}

- (void)barrier{
dispatch_queue_t queue = dispatch_queue_create("12312311",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2.f];
NSLog(@"----1 --------%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
NSLog(@"-----2 ---------%@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
NSLog(@"------barrier---------%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"-------3--------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"--------4--------%@",[NSThread currentThread]);
});

}

- (void)dispatchAfter{
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
NSLog(@"主线程延迟2秒后执行");
});
}

- (void)dealWithMaybeExplode:(BOOL)explode {
dispatch_queue_t queue = dispatch_queue_create("explode",DISPATCH_QUEUE_CONCURRENT);

if (explode) {
for (int i = 0; i < 9999; i++) {
dispatch_async(queue, ^{
NSLog(@"wrong %d ",i);
});
}
}else{
dispatch_apply(9999, queue, ^(size_t i) {
NSLog(@"right %zu ",i);
});
}
}

- (void)dispatchGroupWaitDemo{
dispatch_queue_t queue = dispatch_queue_create("000", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2.f];
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"等待结束");
}

- (void)createDispatchBlock{
dispatch_queue_t queue = dispatch_queue_create("block", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"run block");
});
dispatch_async(queue, block);

//    QOS Way
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
NSLog(@" run qos block'");
});
dispatch_async(queue, qosBlock);
}

- (void)dispatchSemaphoreDemo{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start'");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"semaphore + 1");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"信号使用结束");
}
- (void)deadLockCase1{
NSLog(@"1");
//    主队列的同步线程,按照FIFO的原则 (先入先出),2排在3后面会等3执行完,但因为是同步线程,3又要等待2执行完,所以造成死锁
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}

- (void)deadLockCase2 {
NSLog(@"1");
//   3会等2,因为2在全局并行队列里面,不需要等3,这样2执行完回到主队列,3就开始执行
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
NSLog(@"2");
[NSThread sleepForTimeInterval:2.f];
});
NSLog(@"3");
}

- (void)deadLockCase3 {
dispatch_queue_t  serialQueue = dispatch_queue_create("deadLock", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(serialQueue, ^{
NSLog(@"2");
//        串行队列里面同步一个串行队列就会死锁
dispatch_sync(serialQueue, ^{
NSLog(@"3");
});
//        dispatch_async(serialQueue, ^{
//            NSLog(@"3");
//        });
NSLog(@"4");
});
NSLog(@"5");

//    解决方法:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(serialQueue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}

- (void)deadLockCase4 {
NSLog(@"1");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
//        将同步的串行队列放到另外一个线程就能解决Case3的死锁
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
- (void)deadLockCase5 {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
//        回到主线程,主线程在做while 死循环,后面的操作就没有办法做了。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
});

NSLog(@"4");
//    死循环
while (1) {}
}

- (void)dispatchSource{
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));

dispatch_source_set_event_handler(source, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"更新UI");
});
});
dispatch_resume(source);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"后台加载");
//        通知队列
dispatch_source_merge_data(source, 1);
});
}


关于锁、dispatch_set_context 和 dispatch_get_context、dispatch_suspend 和 dispatch_resume 使用下篇文章会深入将到。

有讲的不对的地方,请大家指出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios 多线程 GCD