一.线程与进程
要理解多线程首先要了解进程与线程的基本概念。
1.进程
进程指的是系统中正在运行的一个应用程序。一个应用一般只会开启一个进层,单有的也会开启多个(比如浏览器),mac的进程的个数可以在活动监视器中查看。
一个进程中的线程是共享内存和资源的。
2.线程
线程是进程的基本单元,进程中的任务都要在线程中执行。(如:下载音乐,播放音乐等)。
每个进程都有一个主线程,主线程的生命周期是和进程一样的,当进程结束时,主线程也就停止了。
3.多线程
多线程是指一个进程中存在多个线程,一般单线程执行任务是串行的,而多线程可以并发执行多个任务(每个线程执行一个任务),所以多线程提高了cpu的利用率,但会使得设计更加复杂。
多线程使用时要注意界面的刷新只能在主线程中进行,所以不要将耗时的任务放在主线程中。
多线程中每个线程的内存大小为都为512kb,要注意内存的管理。(ps:很多地方说主线程是1m,我自己试了一下是512kb,待会在下面会给出程序,如果不对请大家改正)
二.多线程的三种技术
1.NSThread
优点:NSThread量级轻,使用简单
缺点:需要自己管理线程的生命周期,线程同步,加锁等
2.NSOperation
不需要关心线程的管理,数据同步。
NSOperation是面向对象的(可以复用,封装,子类化)抽象程度高,接口简单,可读性强,在项目较复杂的情况下使用较好。
3.GCD
GCD是ios4以后推出的多线程技术,是基于C语言的,简单,明了,结合block使用更加简单,在项目较简单时使用比较好。
三.线程的创建
1.NSThread
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//新建一个线程
//方法一:
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(mutableThread:) object:@"thread"];
[thread1 start]; //手动启动
if([[NSThread currentThread] isMainThread]){
NSLog(@"是主线程 大小为:%ld",[[NSThread currentThread] stackSize]);
}
}
- (void)mutableThread:(NSString*)str{
if([[NSThread currentThread] isMainThread]){
NSLog(@"是主线程 大小为:%ld",[[NSThread currentThread] stackSize]);
}else{
NSLog(@"%@,大小为:%ld",str,[[NSThread currentThread] stackSize]);
}
//手动退出
[NSThread exit];
}
输出结果为:所以我得出主线程的内存为512kb的结论。
NSThread创建线程还可以用一下两种方法
[NSThread detachNewThreadSelector:@selector(mutableThread:) toTarget:self withObject:@“thread1”];
[self performSelector:@selector(mutableThread:) withObject:nil afterDelay:0.5];
2.NSOperation
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建线程池
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = 3;//设置多线程最大并发数
queue.suspended = YES;//所有线程中的任务挂起
//创建NSOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(thread1Action:) object:@"op1"];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread2Action:) object:@"op2"];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread3Action:) object:@"op3"];
NSInvocationOperation *op4 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread4Action:) object:@"op4"];
op1.queuePriority = NSOperationQueuePriorityNormal;
op2.queuePriority = NSOperationQueuePriorityNormal;
op3.queuePriority = NSOperationQueuePriorityHigh;
op4.queuePriority = NSOperationQueuePriorityNormal;
//把operation添加到线程队列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
queue.suspended = NO;
}
- (void)thread1Action:(NSString *)str{
NSLog(@"thread1Action %p",[NSThread currentThread]);
for (int i = 0; i<10; i++) {
NSLog(@"threa1Aciton --- %d",i);
}
}
- (void)thread2Action:(NSString *)str{
NSLog(@"thread2Action %p",[NSThread currentThread]);
for (int i = 0; i<10; i++) {
NSLog(@"threa2Aciton --- %d”,i);
}
}
- (void)thread3Action:(NSString *)str{
NSLog(@"thread3Action %p",[NSThread currentThread]);
for (int i = 0; i<10; i++) {
NSLog(@"threa3Aciton --- %d",i);
}
}
- (void)thread4Action:(NSString *)str{
NSLog(@"thread4Action %p",[NSThread currentThread]);
for (int i = 0; i<10; i++) {
NSLog(@"threa4Aciton --- %d",i);
}
}
输出结果为:从结果可以看出因为设置子线程3的优先级较高,所以虽然子线程1和子线程2先加入到线程池中,但3先执行。同时因为设置线程最大并发数是3,所以在子线程3执行结束之后才执行子线程4。
同时可以用block语法 给队列中添加操作
[queue addOperationWithBlock:^{
NSLog(@"thread1Action %p",[NSThread currentThread]);
for (int i = 0; i<10; i++) {
NSLog(@"threa5Aciton --- %d",i);
}
}];
3.GCD
GCD有串行队列和全局队列
其中串行队列只会开启一条线程 dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
全局队列可能会开启多条线程,同时执行多个任务(无序),开启的线程数取决于任务数量和内核等
一 使用全局队列 并行队列
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
异步添加:立即返回,不等待block执行完毕
dispatch_async(queue1, ^{
for (int i = 0; i<10;i++) {
NSLog(@"demo1 %p ----%d",[NSThread currentThread],i);
}
});
dispatch_async(queue1, ^{
for (int i = 0; i<10;i++) {
NSLog(@"demo2 %p ----%d",[NSThread currentThread],i);
}
});
同步添加: 等待block执行完毕才返回
dispatch_sync(queue1, ^{
for (int i = 0; i<10;i++) {
NSLog(@"demo2 %p ----%d",[NSThread currentThread],i);
}
});
NSLog(@“添加完毕");
需要注意的是不能在串行队列中同步添加block这种延迟调用的任务,容易造成死锁。例如在主线程中同步添加blck来执行任务,那block就会等待队列中的任务结束再执行,而队列就会等待block执行结束。这样就会相互等待。
三.原子操作
原子操作是为了防止多个线程同时使用同一个资源而造成混乱。
下面以车票为例进行说明
@implementation ViewController{
//票个数
int ticket;
}
- (void)viewDidLoad {
[super viewDidLoad];
ticket = 30;
[self performSelectorInBackground:@selector(saleTicket:) withObject:@"thread1"];
[self performSelectorInBackground:@selector(saleTicket:) withObject:@"thread2"];
[self performSelectorInBackground:@selector(saleTicket:) withObject:@"thread3"];
}
- (void)saleTicket:(NSString *)str{
while (1) {
//mutex 信号量
//原子操作 只有一个人可以进来操作,操作过程中不能被打断
@synchronized(self){
NSLog(@"%@ 开始卖票",str);
if (ticket == 0) {
break;
}
ticket--;
NSLog(@"%@ 还剩票----%d",str,ticket);
}
}
}
在用关键字@synchronized标记之后里面的程序在同一个时间只能有一个线程执行,避免了执行紊乱。