GeekBand iOS开发高级进阶学习笔记(第三周)

2016-05-01  本文已影响67人  Hysoka

多线程的概念

进程 Process:是正在运行程序的实例,是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,一个进程中可以有多个线程。

线程 Thread:是程序执行流的最小单元,是进程中的一个实体,被系统独立调度和分派的基本单位。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

在iOS编程中有三套API用来控制多线程操作
1、定义工作同时进行调度的有 POSIX threads 和 NSThread。POSIX threads是C语言的API,NSThread是对POSIX threads的封装,是OC的API。

2、基于一个任务来调度的有NSOperation, NSBlockOperation, NSInvocationOperation

3、还有一套是用来管理进程队列的,NSOperationQueue和Dispatch Queue。Dispatch Queue也是基于C语言的API使用比较灵活,也是目前最长用的一套API。

多线程的API

MainThread
程序启动后,系统会自动创建一个主线程,也叫UI线程。如果主线程阻塞,UI就会失去响应,所有的UI操作都应该放到主线程中去执行。因此为了不妨碍UI执行的流畅程度,时间长、计算量大、阻塞IO都应该另外开辟分线程来执行。

用NSThread开新线程执行
新开线程的方法
+(void)detachNewThreadSelector:(SEL) toTarget:(id) withObject:(id)
也可以用自定义NSThread实例
在NSThread的子类里重载 -(void)main
-initWithTarget:(id) selector:(SEL) object:(id)
[thread start]
回主线程中执行

-(void)[anyNSObject performSelectorOnMainThread:(SEL) withObject:(nullable id) waitUntilDone:(BOOL)]
+(BOOL)isMainThread  //返回一个值判断是否为主线程

创建线程也是有开销的

创建线程的开销

线程属性及线程内部存储(TLS)
.name 线程的名字
.stackSize 设置线程栈的大小,单位是字节(byte)由于内存对齐的原因必须设置为4K的整数倍,要在线程启动前设置才起作用。
threadPriority 线程的优先级设置[0.0 1.0]
TLS
NSMutableDictionary *thread.threadDicitionary //线程的存储空间

线程控制
启动指定线程
start 开始线程
canecl 取消开始线程

停止当前线程
+currentThread 获取当前线程
+sleepUntilDate:(NSDate *) 线程休眠(到指定的时间)
+sleepForTimeInterval:(NSTimeInterval) 线程休眠(指定秒数)
+exit 强行停止线程,尽量少用

线程与事件响应
除了Touches,还有网络、定时器等其他事件,等候这些事件出现时,不能锁住界面,然而线程不停轮询所有事件源有太浪费,我们可以将计算机调度与事件查询分开。体统将事件查询功能交给NSRunLoop来做。

NSRunLoop RunLoop Mode

每一个RunLoop可以有多个Mode每一个Mode都有3个集合,分别是Source,Observer,Timer

预定义的RunLoop Mode

Common Modes
加到这个模式里的Item,会关联到该RunLoop的所有Mode上。保证了所有Mode中这个Item都会被触发。
自定义Mode可以标记自己是Common的,调用CFRunLoopAddCommonMode()这个方法会把这个Mode名字加到_commonModes Set里。
系统提供的mode都是Common Modes。

NSRunLoop的生命周期


rllt.jpg

主线程的观察者会在进入RunLoop时创建一个autorelease pool,当线程即将睡眠的时候主线程的观察者会创建一个新的autorelease pool,并将旧的autorelease pool释放掉,下一轮就用新的autorelease pool。当这一轮的RunLoop退出时,主线程的观察者会释放掉autorelease pool。
主线程会注册一个Source1以接收HIDEvent。
当线程将要进入睡眠时或RunLoop要退出时,主线程的观察者会遍历界面,将有变动的界面重新布局和绘制。
受其他动作的影响,Timer的触发事件并不精确,受动作影响如果Timer的时间点已经超过,就会跳过这次响应。

通过NSObject给线程添加任务

NSOpertaion
我们使用线程是为了完成一个费时的任务,且不影响UI响应,NSOperation是对线程任务进行了封装:
用selector编写任务:NSInvocationOperation
用block编写任务:NSBlockOperation
任务可以用依赖关系串起来
NSOperationQueue:封装线程管理部分

NSOperation是一个抽象基类,不可以直接使用,必须使用其子类。
执行动作
向NSOperation发送start消息用来 启动(在当前线程中执行)
其内部的执行就是设置状态然后调用main方法
向NSOperation发送cancel消息用来 取消
waitUntilFinished

任务定义
main 方法默认什么也不干,但是会在NSOperation提供的AutoreleasePoo里运行
定义NSOperation子类时覆盖

NSInvocationOperation
创建

-initWithTarget:(id) selector:(SEL) object:(id)
-initWithInvocation:(NSInvocation *) 
//NSInvocation *是一个由(id) selector:(SEL) object:(id)组成的对象

NSBlockOperation
创建
+blockOperationWithBlock:(void (^) (void))
追加
-(void)addExecutionBlock:(void(^)(void))
Operation会copy参数block

NSOperation可以使用依赖关系
-(void)addDependency:(NSOperation *)dep
dep完成后,本Operation才会开始执行(注意不要写成循环依赖)。依赖链中的Operation即使已经完成,也不会从链中移出,除非调用-removeDependency。

NSOperation的状态
.name 名字
.ready 准备好开始
.executing 运行
.finished 运行结束
.cancelled 取消
.asynchronous 异步或同步
默认是NO,只读,只有子类能修改
synchronized, in calling thread
-setCompletionBlock:(^(void))

NSOperationQueue
Queue operations 队列操作
add 将一个NSOperation添加到NSOperationQueue中

[q addOperation:anOp]; //添加一个NSOperation
[q addOperations:anArrayofOps waitUntilFinished:NO]; //添加一个NSOperation集合
[q addOperationWithBlock:^{...}];

运行控制

-setMaxConcurrentOperationCount: //设置线程队列里最多可以有几个任务
-addDependency //添加一个运行依赖
-cancelAllOperation //将队列中的所有任务全部取消
-setSuspended:(BOOL) //队列本身暂停
waitUnitlAllOperationAreFinished //当队列中的所有任务执行完毕后我们可以在用这个方法再添加操作

Main Queue
+mainQueue 调用该方法就可以得到主线程的对象
operations will serially run on main thread in common runloop mode

多线程 GCD
GCD是Grand Central Dispatch的缩写,他是一个C语言API,是Apple开发的一个多核编程的解决方法。
dispatch queue分成Main queue, global dispatch queue, serial queue三种。
用法:获取一个queue,然后dispatch a block using the queue(使用分发模块的方法使用队列)。
在Objective-C里,dispatch objects也是ObjectC对象,支持ARC。

获取一个队列:
Global Concurrent Queue:
dispatch_get_global_queue(id,0)
这个函数返回一个dispatch_queue_t类型,第二个参数永远是0(设计时为了以后扩展,目前填0)
第二个参数id:填写的是任务的优先级,从高到低是:DISPATCH_QUEUE_PRIORITY_HIGH/DEFAULF/LOW/BACKGROUND
也有新的表达方式依然是优先级从高到低参数为:QOS_CLASS_USER_INTERACTIVE/INITIATED/UTILITY/BA CKGROUND

Main Queue(serial)

dispatch_get_main_queue(); //将主线程拿回来

~~dispatch_main(); ~~在MAC OS上调用,iOS编程用不到。

Create Queue
我们自己创建的只能是串行队列
dispatch_queue_create("com.my.q",NULL);
这里第一个参数是队列的名字(是C语言字符串)

Queue OP
暂停和恢复运行一个队列
dispatch_suspend()/diepatch_resume()

在队列退出时进行调用,可以做一些清理的工作
on queue cleanup:dispatch_set_finalizer_f(func)
-void myFinalizerFunction(void *context)

拿到队列后我们可以开始分配任务
dispatch_sync(q,block): don't do this on q's thread 用的比较少,当前的block会等待线程完成后再返回
dispatch_async(q,block) 将block放入队列后就返回
on task complete:dispatch a block at end of task
dispatch inside
dispatch_retain(q)before queue the task
dispatch_release(q)before queue complete block

get queue in block: dispatch_get_current_queue() 获取当前执行的队列

用Context传递参数,可以给队列定义一个Context来传递数据

dispatch_set_context(q,voidPtr)
dispatch_get_context(q,voidPtr)

singleton 确保任务只执行一次
dispatch_once(dispatch_once_t *,block);

timer 在一段时间后开始执行
dispatch_after(dispatch_time_t, q, block);

for-loop 循环执行
dispatch_apply(count,q,^(size_t i){...})

Dispatch Group

dispatch_group_tgroup = dispatch_group_create();  //创建一个组
dispatch_group_async(g, q,^{});
dispatch_gruop_wait(g) //在这个组的任务全部完成后开始动作

同步
dispatch_semaphore_t 信号量

dispatch_semaphore_create(long) 
long dispatch_semaphore_wait(sema, timeout); 
dispatch_semaphore_signal(sema)

barrier 临界区

dispatch_barrier_async(q,block)
dispatch_barrier_sync(q,block)

Dispatch Source

Dispatch Source

分享一个GCD的代码例子:http://www.cnblogs.com/pure/archive/2013/03/31/2977420.html

上一篇下一篇

猜你喜欢

热点阅读