多线程
Mac、iPhone的操作系统OS X、iOS根据用户的指示启动应用程序后,首先便将包含在应用程序中的CPU命令列配置到内存中。CPU从应用程序知道的地址开始,一个一个执行CPU命令列。
在OC的if或for语句等控制语句或函数调用的情况下,执行命令列的地址会远离当前的位置(位置迁移)。但是,由于一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,因此通过CPU执行的CPU命令列就好比一条无分叉的大道,其执行不会出现分歧。
通过CPU执行的CPU命令列.png
这里所说的“1个CPU执行的CPU命令列为一条无分叉路径”,即为“线程”
这种无分叉路径不只1条,存在有多条时即为“多线程”。1个CPU核执行多条不同路径上的不同命令。
线程在多线程中执行CPU命令列.png
OS X和iOS的核心XNU内核在发生操作系统事件时(如每隔一定时间,唤起系统调用等情况)会切换执行路径。例如CPU的寄存器等信息保存到各自路径专用的内存块中,从切换目标路径专用的内存块中,复原CPU寄存器等信息,继续执行切换路径的CPU命令列。这被称为“上下文切换”
由于使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此看上去就好像1个CPU核能够并列地执行多个线程一样。而且在具有多个CPU核的情况下,就不是“看上去像”了,而是真的提供了多个CPU核并行执行多个线程的技术。
这种利用多线程编程的技术就被称为“多线程编程”
但是,多线程编程实际上是一种易发生各种问题的编程技术。比如多个线程更新相同资源会导致数据的不一致(数据竞争)、停止等待事件的线程会导致多个线程相互等待(死锁)、使用太多线程会消耗大量内存等。
多线程编程导致的问题*多线程技术
多线程简单来说是指一个进程中开启多个线程,多个线程并发执行(同时执行,并行)的技术。例如一个进程中开启三个线程同时执行3个不同任务:
多线程并行操作*多线程原理
对于单核CPU而言,同一时间只能执行一个线程,只能允许一个线程工作。多线程并发是指在一个进程中开启了多个线程,CPU在各个线程之间来回调度。当CPU的调度时间足够快时就产生了多线程并发执行的效果。
*多线程技术的有优点:
1、可以充分利用多核CPU的性能,提供资源的利用率
2、能够提高程序的运行效率,使程序响应更快。
3、能够设置不同任务的执行优先级,
*多线程技术的缺点 (数据竞争、死锁、太多线程导致消耗大量的内存)
1、线程的创建需要CPU的开销,对线程的管理需要额外的CPU开销。线程的使用会为系统带来上下文环境切换带来额外的负担。线程越多,CPU在线程调度管理上的开销就越大,对于移动设备尤甚。
2、线程之间的通信和数据交互复杂。
3、多个线程直接对于资源的使用会导致程序变慢,同时会导致资源的竞争,数据的共享等不安全问题。
。。。
多线程数据安全问题
线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
线程不安全线程安全:简单来说就是多个线程同时对共享资源进行访问时,采用了加锁机制,当一个线程访问共享资源,对该资源进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。
线程安全**线程的几种存在状态
线程几种状态的切换
新建状态(New):当新创建一个线程时,此时线程处在新建状态。程序还没有开始执行线程中的代码。
就绪状态(Runnable):一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start方法创建线程运行的系统资源,这时候线程就处于就绪状态。
运行状态(Running):当线程获得CPU时间后,它才进入运行状态,真正开始执行线程中的代码。
阻塞状态(Blocked):当线程在运行中遇到:1,调用sleep方法;2,线程调用一个在I/O上被阻塞的操作;3,线程等待同步锁;4,线程在等待某个触发条件等几种情况时,线程就处于阻塞状态。 所谓阻塞状态是正在运行的线程没有运行结束,但是不被CPU调度,暂时让出CPU资源,这时其他处于就绪状态的线程就可以获得CPU调度,进入运行状态。
死亡状态(Dead): ** 当线程任务执行完毕或者异常时,线程处于死亡状态。死亡后的线程将不能在执行任务。
iOS中多线程编程的实现方案有一下几种:pthread、NSThread、GCD、NSOperation。
GCD
Grand Central Dispatch(牛逼的中枢调度系统),苹果公司利用C语言开发的多线程实现技术,旨在通过充分利用设备的多核处理器以优化提升应用程序的运行。
GCD的使用步骤:
创建任务
将任务存放到队列中(GCD根据队列的FIFO原则自动将队列中的任务取出,放到对应的线程中执行)
GCD中三个重要的概念:
任务,就是需要执行的操作;
队列,就是存放任务的容器,其中队列又被分为并发队列、串行队列两种。
同步函数/异步函数
GCD中队列和函数:
并发队列(Concurrent Dispatch Queue)
允许多个任务并发(同时)执行的队列
//创建一个并发队列,标志为 www.cqut.queue
dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
串行队列(Serial Dispatch Queue)
串行队列中的任务只能依次执行
//创建一个串行队列,标志为 serial.queue
dispatch_queue_t queue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);
同步函数
在当前线程执行任务,不会开启新的线程
//同步函数,传入队列,执行Block中的任务
dispatch_sync(dispatch_queue_t _Nonnull queue, ^(void)block)
异步函数
可以开启新线程并在其中执行任务
//异步函数,传入队列,执行Block中的任务
dispatch_async(dispatch_queue_t _Nonnull queue, ^(void)block)
注意:同步/异步决定是否开启线程;并发/串行决定任务的执行方式,允不允许并发。根据函数同步/异步调用和任务放置在并发还是串行队列中我们可以得到下面四种组合:
异步函数 + 并发队列
开启多个子线程同时执行多个任务(任务并行)。
异步函数 + 串行队列
开启一个子线程,任务在该线程中依次执行(任务串行)。
同步函数 + 并发队列
不会开启子线程,此时的队列由于没有子线程支持,失去了并发的能力,任务在当前线程中依次执行(任务串行)。
同步函数 + 串行队列
不会开启子线程,任务在队列中依次执行(任务串行)。
GCD中两个特殊的队列:
dispatch_get_main_queue():主队列
GCD默认创建,只需要使用。该函数返回绑定到主线程的默认串行队列。该队列中执行的任务必定是在主线程中执行的。
dispatch_get_global_queue(long identifier, unsigned long flags):全局并发队列
【参数说明】flags:保留在未来使用,默认传0;
identifier: 分配到队列的项目运行的优先级,优先级由高到低依次为:DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND,一般我们使用默认的DISPATCH_QUEUE_PRIORITY_DEFAULT就能满足需求
该函数返回
下面一张图简单概括一下关于同步/异步函数和各种队列之间排列组合子线程开启于任务执行情况:
队列与线程.png
由图可知开发中最常用的就是异步函数组合并发和串行队列,
【注意】:使用同步函数(sync)向当前串行队列中添加任务会卡住当前队列,导致任务不能正常执行。
GCD中常用的函数
dispatch_barrier_async:在该函数之前的任务必定先执行,在该函数之后的任务后执行(当前队列不能是全局并发队列)。
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
//并发队列
dispatch_queue_tqueue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
//执行下面的异步函数
dispatch_async(queue, ^{
NSLog(@"==1==%@",[NSThreadcurrentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"==barrier==%@",[NSThreadcurrentThread]);
});
dispatch_async(queue, ^{
NSLog(@"==2==%@",[NSThreadcurrentThread]);
});
}
//延时执行
//在主队列的delayInSeconds秒后执行任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delayInSeconds *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
//一次性代码执行
//Block中的代码保证在程序运行中只执行一次,默认是线程安全的
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
NSLog(@"onceToken");
});
快速迭代(遍历)
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//快速遍历
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"-%zd-%@",index,[NSThread currentThread]);
});
队列组
//创建队列组
dispatch_group_t group = dispatch_group_create();
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//执行任务一
dispatch_group_async(group, queue, ^{
NSLog(@"任务一");
});
//执行任务二
dispatch_group_async(group, queue, ^{
NSLog(@"任务一");
});
//任务一和任务二执行完毕后在打印结果
dispatch_group_notify(group, queue, ^{
NSLog(@"结果");
});
说明:将异步执行的任务一和任务二放到队列组中,两个完成执行后再执行打印结果的代码。
资源链接链接:http://www.jianshu.com/p/8a7ebecc06f8
网友评论