多线程的概念
进程 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。
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来做。
每一个RunLoop可以有多个Mode每一个Mode都有3个集合,分别是Source,Observer,Timer
Common Modes
加到这个模式里的Item,会关联到该RunLoop的所有Mode上。保证了所有Mode中这个Item都会被触发。
自定义Mode可以标记自己是Common的,调用CFRunLoopAddCommonMode()这个方法会把这个Mode名字加到_commonModes Set里。
系统提供的mode都是Common Modes。
NSRunLoop的生命周期
主线程的观察者会在进入RunLoop时创建一个autorelease pool,当线程即将睡眠的时候主线程的观察者会创建一个新的autorelease pool,并将旧的autorelease pool释放掉,下一轮就用新的autorelease pool。当这一轮的RunLoop退出时,主线程的观察者会释放掉autorelease pool。
主线程会注册一个Source1以接收HIDEvent。
当线程将要进入睡眠时或RunLoop要退出时,主线程的观察者会遍历界面,将有变动的界面重新布局和绘制。
受其他动作的影响,Timer的触发事件并不精确,受动作影响如果Timer的时间点已经超过,就会跳过这次响应。
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
分享一个GCD的代码例子:http://www.cnblogs.com/pure/archive/2013/03/31/2977420.html