标签:
多线程
每个NSThread对象对应一个线程,真正最原始的线程。
优点:轻量级最低,相对简单。
缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等。
自带线程管理的抽象类。
优点:自带线程周期管理,操作上可更注重自己逻辑。
缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类
NSInvocationOperation 和 NSBlockOperation。
苹果开发的一个多核心编程的解决方法。开发者不需要再跟线程打交道了,只需要向队列中添加代码块即可。GCD在后端管理着一个线程池。GCD不仅仅决着你的代码块将在哪个线程被执行,并且他还可以根据可用的系统资源对这些线程进行管理。
优点:最高效,避免并发陷阱。
缺点:基于C实现。
这里,主要记录一下GCD的使用。
使用GCD需要考虑的有:任务的执行方法,队列的运行方式。
任务:表示一段执行的代码,对应到代码里就是一个block。
队列:1.队列当中放的是各种待执行的任务。
队列对应到代码里是一个dispatch queue t对象。可能在非ARC下我们需要手动管理内存,但是个人觉得那些逝去的技术,就让他沉睡在过去吧,况且我也并不想手动的去管理这些与开发不相关的东西,让ARC替我去搞定吧,哈哈。当然内存管理是很重要的,之后我也会再在内存管理方面进行深入的学习,并开一篇关于内存管理的文章。言归正传,ARC下通常队列声明为strong就ok。
异步(加入队列的任务会在另外的线程中执行,一旦任务被提交到队列,就立刻返回)
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
第一个API的任务以block的方式加入队列当中,第二个API以函数的形式把任务加入队列当中。二者在执行功能上并没有什么区别,都是将任务提交到队列当中就立即返回。
NSLog(@"this is main queue, i want to throw a task to global queue");
dispatch_queue_t globalQueue = dispatch_queue_create("com.liancheng.global_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(globalQueue, ^{
// task
});
NSLog(@"this is main queue, throw task completed");
同步(任务被加入队列之后会阻塞主线程,不会立即返回,需等任务完成之后再返回)
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
NSLog(@"this is main queue, i want to throw a task to global queue");
dispatch_queue_t globalQueue = dispatch_queue_create("com.liancheng.global_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(globalQueue, ^{
// task
});
NSLog(@"this is main queue, throw task completed");
dispatch_queue_t queue = dispatch_queue_create("com.liancheng.serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 到达串行队列
dispatch_sync(queue, ^{ //发生死锁
});
});
在串行队列当中的一个任务,以同步方式提交了一个任务到自己的队列当中,造成锁。
众所周知,串行队列中的任务一个一个执行,当执行任务的过程当中,A任务向自己这个队列当中添加了一个B任务,并且还是以同步方式提交,同步方式提交的B任务就要立刻执行(因为是同步嘛),可是A任务还没有执行完毕,由此造成死锁。
如果队列是并行队列,就不会发生这种情况了。或者把B任务加到其他的队列当中。
dispatch_queue_t dispatch_get_main_queue(void)
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
这种方式获取的队列都市并行队列,并且队列不能修改。
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
使用dispatchbarrier将任务加入到并行队列之后,任务会在前面任务全部执行完成之后执行,任务执行过程中,其他任务无法执行,直到barrier任务执行完成。
4个API
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
(以下来自Objc中国)
使用并发编程会带来许多陷阱。只要一旦你做的事情超过了最基本的情况,对于并发执行的多任务之间的相互影响的不同状态的监视就会变得异常困难。
并发编程的许多问题的根源就是在多个线程当中访问共享资源。 资源可以是一个属性,通用的内存,网络设备或者一个文件等等。
以一张图片说明资源共享所引发的问题。
在实际的开发中,情况甚至要比上面的情况更加复杂,因为现代 CPU 为了优化目的,往往会改变向内存读写数据的顺序(乱序执行)。
互斥访问的意思就是同一时刻,只允许一个线程访问某个特定资源。为了保证这一点,每个希望访问共享资源的线程,首先需要获得一个共享资源的互斥锁,一旦某个线程对资源完成了操作,就释放掉这个互斥锁,这样别的线程就有机会访问该共享资源了。
虽然出于安全性的考虑,Objective-C将所有的属性都默认设置为原子操作,是的所有的属性都能支持互斥锁。(atomic,在之后的关于多线程的学习中会深入讨论原子操作)。但是要知道加锁的过程是需要昂贵的代价的。
所以,在编程当中,不需要多线程的情况下,并且,即使是在多线程的情况下,如果能确认不会引发资源共享的情况发生,也尽量不要使用原子操作。
互斥锁解决了竞态条件的问题,但很不幸同时这也引入了一些其他问题,其中一个就是死锁。当多个线程在相互等待着对方的结束时,就会发生死锁,这时程序可能会被卡住。
一个资源在两个或者两个以上的线程当中相互使用,第三方线程始终得不到这部分资源。 这部分待之后理解
低优先级的任务先获取到共享资源的锁,高优先级的任务需要等待低优先级的把锁解开后才能去访问共享资源,如果在这个等待过程当中,有一个中优先级的任务不需要块共享资源,那么他就有可能会枪战低优先级的任务而先于高优先级的任务被执行。由于低优先级的任务持有锁,然而他又被中优先级的任务阻塞无法释放锁,高优先级的任务就一直无法被执行,从而能导致优先级翻转。
所以,我们说尽量不要使用不同的优先级,使用的优先级越多,情况就越不好控制,可能出现的问题就越多。
标签:
原文地址:http://blog.csdn.net/qingbaobei_loveyou/article/details/51220590