我只是一个搬运工,仅仅为了加深记忆,感谢做者分享,文章大部分来源:进击的蜗牛君git
几乎全部的操做系统都支持同时运行多个任务,一个任务一般就是一个程序,每一个程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每一个顺序执行流就是一个线程。程序员
当一个程序进入内存运行后,即变成一个进程。进程是处因而处于运行过程当中的程序,而且具备必定的独立功能,进程是系统进行资源分配和调度的一个独立单位。通常而言,进程有以下特征:github
线程也被称作轻量级进程,线程是进程的执行单元。就像进程在系统中同样,线程在进程中也是独立的,并发的执行流程。一个进程能够拥有多个线程,一个线程必须有一个父进程,但再也不拥有系统资源,而是和父进程一块儿共享父进程的所有资源。多线程因为共享父进程的资源,因此编程更加方便,可是也须要当心线程不会影响到父进程中的其余线程。线程是独立运行的,它并不知道其余线程的存在。线程执行是抢占式的,也就是说,当前运行的线程在任什么时候候均可能被挂起,以便林另一个线程能够运行。编程
为了提升资源利用率来提高系统总体效率,实际每每是将耗时操做放在后台执行,避免阻塞主线程,在iOS中UI绘制和用户响应都是主线程。安全
经常使用APIbash
- (void)viewDidLoad {
[super viewDidLoad];
//打印当前线程
NSLog(@"开始:%@ 优先级:%d", [NSThread currentThread], [NSThread currentThread].qualityOfService);
//1.建立NSTread对象,必须调用start方法开始,而且只能传一个参数object
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"test"];
// NSThread *thread = [[NSThread alloc] initWithBlock:^{}];
thread.name = @"testThread";
thread.qualityOfService = NSQualityOfServiceUserInteractive;
[thread start];
//2.直接建立并启动线程
// [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"test"];
// [NSThread detachNewThreadWithBlock:^{}];
//3.隐式直接建立
// [NSThread performSelectorInBackground:@selector(run:) withObject:nil];
// NSLog(@"结束:%@", [NSThread currentThread]);
}
- (void)run:(NSObject *)object {
//阻塞休眠
// [NSThread sleepForTimeInterval:5];
//停止当前线程
// [NSThread exit];
NSLog(@"子线程运行:%@ %@ 优先级:%d", [NSThread currentThread], object, [NSThread currentThread].qualityOfService);
}
复制代码
线程被启动后,并非直接进入执行状态,也不是一直处于执行状态,因为线程并发,线程会反复在运行、就绪间切换。建立一个线程后,处于新建状态,系统为其分配内存,初始化成员变量;调用-(void)start;
方法后,该线程处于就绪状态,系统为其建立方法调用栈和程序计数器,此时并无运行,什么时候运行取决于系统调度。 多线程
每一个线程都有必定的优先级,优先级越高得到执行机会越多。目前经过qualityOfService
属性来设置,原来的threadPriority
因为语义不够清晰,已经被废弃了。并发
NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操做,好比处理点击事件,绘制图像到屏幕上
NSQualityOfServiceUserInitiated:次高优先级,主要用于执行须要当即返回的任务
NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级
NSQualityOfServiceUtility:普通优先级,主要用于不须要当即返回的任务
NSQualityOfServiceBackground:后台优先级,用于彻底不紧急的任务
复制代码
使用NSThread进行多线程编程较复杂,须要本身控制多线程的同步、并发,还须要本身控制线程的终止销毁,稍有不留神容易出现错误,对开发者要求较高,通常较少使用。app
iOS还提供了NSOperation与NSOperationQueue来实现多线程,是基于GCD更高一层的封装,彻底面向对象。可是GCD更简单易用、代码可读性也更高。异步
NSOperationQueue:负责管理系统提交的多个NSOperation,底层维护了一个线程池。不一样于GCD中的调度队列FIFO(先进先出)原则。NSOperationQueue对于添加到队列中的操做,首先进入准备就绪的状态(就绪状态取决于操做之间的依赖关系),而后进入就绪状态的操做的开始执行顺序(非结束执行顺序)由操做之间相对的优先级决定(优先级是操做对象自身的属性)。
NSOperation: 表明一个多线程任务。
NSOperationQueue *queue;
//获取执行当前NSOperation的NSOperationQueue队列
// queue = [NSOperationQueue currentQueue];
//获取主线程的NSOperationQueue队列
// queue = [NSOperationQueue mainQueue];
//自定义队列
queue = [[NSOperationQueue alloc] init];
//队列名
queue.name = @"testOperationQueue";
//最大并发操做数(系统有限制,即便设置很大,也会自动调整)
queue.maxConcurrentOperationCount = 10;
//设置优先级
queue.qualityOfService = NSQualityOfServiceDefault;
//自定义NSOperation,若是SEL和Block为空,系统不会加入到指定队列
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockOperation");
}];
//添加依赖关系,invocationOperation执行完后才执行blockOperation
[blockOperation addDependency:invocationOperation];
//添加到队列中
// [queue addOperation:invocationOperation];
[queue addOperations:@[invocationOperation, blockOperation] waitUntilFinished:NO];
//直接添加代码块任务
[queue addOperationWithBlock:^{
}];
//打印全部的NSOperation
for(int i=0; i<queue.operationCount; i++) {
NSLog(@"队列%@的第%d个NSOperation:%@", queue.name, i, queue.operations[i]);
}
//终止全部NSOperation
// [queue cancelAllOperations];
//执行完全部NSOperation才能解除阻塞当前线程
// [queue waitUntilAllOperationsAreFinished];
复制代码
注:队列的串行和并行决定了任务以何种方式执行,执行的异步和同步决定了是否须要开辟新线程处理任务。
/** 获取队列 */
//获取指定优先级的全局并发队列(flag填0便可,仅预留的参数,使用其余值可能会返回null)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//建立自定义并行队列
dispatch_queue_t queue1 = dispatch_queue_create("testQueue1", DISPATCH_QUEUE_CONCURRENT);
//获取系统主线程关联的串行队列
dispatch_queue_t queue2 = dispatch_get_main_queue();
//建立自定义串行队列
dispatch_queue_t queue3 = dispatch_queue_create("testQueue3", DISPATCH_QUEUE_SERIAL);
/** 提交任务 */
//异步提交代码块到并发队列
dispatch_async(queue, ^{
});
//同步提交代码块到自定义并发队列
dispatch_sync(queue1, ^{
});
//异步提交代码块到串行队列,线程池将在指定时间执行代码块(实际是5秒后加入到队列中,实际并不必定会立马执行,通常精度要求下是没问题的)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), queue2, ^{
});
//异步提交代码到自定义串行队列,同步函数,不管是在串行仍是并行队列中执行,都要执行完才返回,因此要防止线程阻塞和死锁,time表示当前是第几回(若是提交给并发队列,会启动五个线程来执行)
dispatch_apply(5, queue3, ^(size_t time) {
});
//实际是个long类型变量,用于判断该代码块是否被执行过
static dispatch_once_t onceToken;
//主线程执行一次代码块
dispatch_once(&onceToken, ^{
});
//等group执行完后,才能执行下一步
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
/** 组(用于须要等待多个任务所有执行完再进行下一步) */
dispatch_group_t group = dispatch_group_create();
//并发执行的代码块1
dispatch_group_async(group, queue, ^{
});
//并发执行的代码块2
dispatch_group_async(group, queue, ^{
});
//等待两个代码块执行完汇总
dispatch_group_notify(group, queue, ^{
});
/** 栅栏(用于须要依次执行完多个线程组) */
//并发队列异步执行代码块1,2
dispatch_async(queue, ^{
//代码块1
});
dispatch_async(queue, ^{
//代码块2
});
//1,2执行完后才会执行3,4
dispatch_barrier_async(queue, ^{
});
//并发队列异步执行代码块3,4
dispatch_async(queue, ^{
//代码块3
});
dispatch_async(queue, ^{
//代码块4
});
/** 信号量(用于控制线程的等待和执行) */
//建立信号量,value表示初始信号总量,支持多少个操做来执行
dispatch_semaphore_t t = dispatch_semaphore_create(1);
//发送一个信号,让信号总量+1
dispatch_semaphore_signal(t);
//使信号总量-1,若是总量为0,则会一直等待(阻塞所在线程),直到总量大于0则继续执行
dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);
/*1.能够将异步执行变为同步执行,如须要等待下载完后再直接返回数据(咱们也能够经过block回调)*/
//总信号量设置为0
dispatch_semaphore_t t1 = dispatch_semaphore_create(0);
//执行耗时代码
void (^downloadTask)(void) = ^ {
//下载图片
...
...
//完成后发送信号量
dispatch_semaphore_signal(t1);
};
downloadTask();
//一直等到信号量计数为1才执行下一步,也就是等到图片下载完后
dispatch_semaphore_wait(t1, DISPATCH_TIME_FOREVER);
/*2.保证线程安全*/
//设置信号量初始计数为1,保证只能有一个操做能进来
dispatch_semaphore_t t2 = dispatch_semaphore_create(1);
//至关于加锁,消耗使用计数,若是已经被一个线程使用,后续只能挂起等待信号量回复
dispatch_semaphore_wait(t2, DISPATCH_TIME_FOREVER);
//执行业务代码
...
...
//解锁
dispatch_semaphore_signal(t2);
/*3.模拟NSOperationQueue的最大并发操做数*/
//最大并发操做支持10
dispatch_semaphore_t t3 = dispatch_semaphore_create(10);
//剩余操做同上,其实就是相似于将NSOperationQueue的maxConcurrentOperationCount设置为10
复制代码
在App程序进入后台时,咱们应该尽可能释放内存和保存用户数据或者状态信息。在默认状况下,应该仅在5秒钟处理这些工做,咱们能够经过UIApplication
的beginBackgroundTaskWithExpirationHandler
方法来申请延长处理时间,最多有十分钟。
- (void)applicationDidEnterBackground:(UIApplication *)application {
//声明关闭后台任务代码块
void (^endBackgroundTask)(UIBackgroundTaskIdentifier backgroudTask) = ^(UIBackgroundTaskIdentifier backgroudTask) {
[[UIApplication sharedApplication] endBackgroundTask:backgroudTask];
backgroudTask = UIBackgroundTaskInvalid;
};
//开启后台任务
__block UIBackgroundTaskIdentifier backgroudTask;
backgroudTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
//十分钟内仍然没有完成,系统处理终止句柄
endBackgroundTask(backgroudTask);
}];
//执行相关代码
//结束后台任务
endBackgroundTask(backgroudTask);
}
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"%@", [NSThread currentThread]);
});
}
复制代码
在主队列中增长同步代码块,就会形成死锁,因为同步是须要当即顺序执行的,上述代码中,Block中的方法须要在viewDidLoad
结束后才能完成,可是viewDidLoad
想要结束又必须先结束Block中的方法,因此相互永久等待,形成了死锁。
直接使用GCD的相关API通常是不会的,block结束后没有循环引用的条件,YYKit的issues下有个有去的讨论:dispatch_async的block里面须要_weak self吗?
线程安全主要是因为系统的线程调度具备必定的随机性形成的,因为是多并发,多个线程同时对一份数据进行读写,就可能在读取执行通常的时候另一个线程去写入,致使数据异常。线程安全即保证线程同步
为了解决这个问题,Objective-C的多线程支持引入同步,使@synchronized
修饰代码块,被修饰的代码块可简称为同步代码块,语法格式以下
@synchronized (obj) {
//同步代码块
}
复制代码
其中obj
就是同步监视器,当一个线程执行同步前,必须先得到同步监视器的锁定,任什么时候刻只能有一个线程得到锁定,执行完成后,才会释放,若是此时有新的线程访问,那么新线程会进入休眠状态。一般推荐使用可能被并发访问的共享资源做为同步监视器。
1. OSSpinLock(自旋锁)
#import <libkern/OSatomic.h>
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//尝试加锁(若是须要等待就不加锁,直接返回false;若是不须要等待加锁,返回true)
bool resule = OSSpinLockTry(&lock);
//加锁
OSSpinLock(&lock);
//解锁
OSSpinLockUnlock(&lock);
复制代码
2. os_unfair_lock
#import <os/lock.h>
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//尝试加锁
os_unfair_lock_trylock(&lock);
//加锁
os_unfair_lock_lock(&lock);
//解锁
os_unfair_lock_unlock(&lock);
复制代码
3. pthread_mutex
互斥锁
#import <pthread.h>
//初始化锁的属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);
//初始化
pthread_mutex_t mutex;
pthread_mutex_init (&mutex,&attr);
//尝试加锁
pthread_mutex_trylock (&mutex);
//加锁
pthread_mutex_lock (&mutex);
//解锁
pthread_mutex_unlock (&mutex);
//销毁相关资源
pthread_mutexattr_unlock(&attr);
pthread_mutex_destroy(&mutex);
复制代码
递归锁
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
复制代码
条件
// 初始化锁
pthread_mutex_t mutex;
//NULL表明使用默认属性
pthread_mutex_init(&mutex, NULL);
// 初始化条件
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
//等待条件(进入休眠,放开mutex锁;被唤醒后,会再次对mutex加锁)
pthread_cond_wait(&cond, &mutex);
//激活一个等待条件的线程
pthread_cond_signal(&cond);
//销毁资源
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
复制代码
4. NSLock、NSRecursiveLock
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking>
{
- (BOOL)tryLock;
- (BOOl)lockBeforeDate:(NSDate *)limit;
}
@end
//初始化锁
NSLock *lock = [[NSLock alloc] init];
复制代码
5. NSCondition
@interface NSCondition : NSObject <NSLocking>
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
复制代码
6. NSConditionLock
@interface NSConditionLock : NSObject <NSLocking> {
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
}
@end
复制代码
7. dispatch_semaphore
//信号量的初始值
int value = 1;
//初始化信号量
dispatch_semaphore semephore = dispatch_semaphore_creat(value);
//若是信号量的值<=0,当前线程就会进入休眠等待(直到信号量的值>0)
//若是信号量的值>0, 就减1,而后往下执行后面的代码
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//让信号量的值加1
dispatch_semaphore_signal(semaphore);
复制代码
8. dispatch_queue
dispatch_queue_t queue = dispatch_queue_creat("lock_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//任务
})
复制代码