iOS 线程详解,彻底学会线程

有人问我线程的问题,我简单总结一下。
要知其然,更要知其所以然

首先,什么是线程?

一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。
iOS中的程序启动,创建好一个进程的同时,一个线程便开始运作,这个线程叫做主线程。主线成在程序中的位置和其他线程不同,它是其他线程最终的父线程,且所有的界面的显示操作即AppKit或UIKit的操作必须在主线程进行。
系统中每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则公用进程的内存空间。
每创建一个新的进成,都需要一些内存(如每个线程有自己的stack空间)和消耗一定的CPU时间。
当多个进成对同一个资源出现争夺的时候需要注意线程安全问题

  • 网络传输方式
    a:同步:所有任务放在一个线程中完成,只要当前的任务没有完成 那么下一个任务就处于阻塞的状态
    b:异步:多个任务放在多个线程中完成 即使当前任务没有完成 也不会永祥其他任务的执行

  • 数据传输方式
    a: 串行:一个线程中只有当前任务完成以后下一个任务才能执行
    b: 并发:多个线程完成不同的任务 互不干扰

1.NSThread

//只需要创建线程 不需要手动开启线程
        //线程对象只要被创建  就自动开始执行任务
        [NSThread detachNewThreadSelector:@selector(threadMain1:) toTarget:self withObject:num];

或者

NSNumber *num = [NSNumber numberWithInt:100];
//创建线程
        //2。
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:num];
        //必须手动开启线程
        [thread start];

//只要通知结束就会调用threadExit:这个方法 这个方法会被调用(有几个线程,调用几次)

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExit:) name:NSThreadWillExitNotification object:nil];

// UI刷新
[self performSelectorOnMainThread:@selector(refreshUI) withObject:nil waitUntilDone:NO];

注释:资源争夺的时候,比如卖火车票,不能一张卖多人,就需要加锁 lock 完事了unlock

2.NSOperation

2.1.创建队列的对象
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
2. 2.设置队列中盛放线程的最大个数
[queue setMaxConcurrentOperationCount:4];
2. 3.队列中得优先级都是平等的

 //(1)创建单独子线程
    NSInvocationOperation *test1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(threadMain1:) object:nil];
    //线程执行结束以后触发先面对而方法
    [test1 setCompletionBlock:^{
        NSLog(@"text1线程执行结束");
    }];

//(2).创建block类型的线程
    NSBlockOperation *test2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"test2 i=%d",i);
            [NSThread sleepForTimeInterval:0.1];
        }
    }];
    [test2 setCompletionBlock:^{
        NSLog(@"test2线程执行结束");
        //线程执行结束以后 会自动从队列中移除并且退出
    }];
    [queue addOperation:test2];

2.4.将子线程放在队列中
[queue addOperation:test1];
//子线程放入队列中 就会立即执行线程方法

3.GCD

GCD是基于C语言底层API实现的一套多线程并发机制,非常的灵活方便,在实际的开发中使用很广泛。
简单来说CGD就是把 操作 放在 队列 中去执行。
自动管理线程的生命周期,自动利用更多的CPU内核
3.1. 队列的创建方法

  • 可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
  • 对于并发队列,还可以使用dispatch_get_global_queue来创建全局并发队列。GCD默认提供了全局的并发队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
// 同步执行任务创建方法
dispatch_sync(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 这里放任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 这里放任务代码
});

各两种,所以就有四种情况了

1.并发队列 + 同步执行

不会开启新线程,执行完一个任务,再执行下一个任务

//并发队列 + 同步执行
-(void)GCD1{
    NSLog(@"syncConcurrent---begin");

    dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"syncConcurrent---end");
}

输出

syncConcurrent---begin
1------<NSThread: 0x60000007f200>{number = 1, name = main}
1------<NSThread: 0x60000007f200>{number = 1, name = main}
2------<NSThread: 0x60000007f200>{number = 1, name = main}
2------<NSThread: 0x60000007f200>{number = 1, name = main}
3------<NSThread: 0x60000007f200>{number = 1, name = main}
3------<NSThread: 0x60000007f200>{number = 1, name = main}
syncConcurrent---end

2. 并发队列 + 异步执行

  • 可同时开启多线程,任务交替执行
 NSLog(@"asyncConcurrent---begin");

    dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"asyncConcurrent---end");

输出

asyncConcurrent---begin
asyncConcurrent---end
1------<NSThread: 0x60000007e680>{number = 3, name = (null)}
2------<NSThread: 0x608000079fc0>{number = 4, name = (null)}
3------<NSThread: 0x60800007b140>{number = 5, name = (null)}
1------<NSThread: 0x60000007e680>{number = 3, name = (null)}
2------<NSThread: 0x608000079fc0>{number = 4, name = (null)}
3------<NSThread: 0x60800007b140>{number = 5, name = (null)}
  • 除了主线程,又开启了3个线程,并且任务是交替着同时执行的。
  • 所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始异步执行。

3. 串行队列 + 同步执行

  • 不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
 NSLog(@"syncSerial---begin");

    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });    
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"syncSerial---end");

输出

syncSerial---begin
1------<NSThread: 0x608000064a00>{number = 1, name = main}
1------<NSThread: 0x608000064a00>{number = 1, name = main}
2------<NSThread: 0x608000064a00>{number = 1, name = main}
2------<NSThread: 0x608000064a00>{number = 1, name = main}
3------<NSThread: 0x608000064a00>{number = 1, name = main}
3------<NSThread: 0x608000064a00>{number = 1, name = main}
syncSerial---end
  • 所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行。
  • 所有任务都在打印的syncConcurrent—begin和syncConcurrent—end之间,这说明任务是添加到队列中马上执行的

4. 串行队列 + 异步执行
- 会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务

 NSLog(@"asyncSerial---begin");

    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"asyncSerial---end");

输出

asyncSerial---begin
asyncSerial---end
1------<NSThread: 0x608000267800>{number = 3, name = (null)}
1------<NSThread: 0x608000267800>{number = 3, name = (null)}
2------<NSThread: 0x608000267800>{number = 3, name = (null)}
2------<NSThread: 0x608000267800>{number = 3, name = (null)}
3------<NSThread: 0x608000267800>{number = 3, name = (null)}
3------<NSThread: 0x608000267800>{number = 3, name = (null)}
  • 开启了一条新线程,但是任务还是串行,所以任务是一个一个执行。
  • 所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

But别忘记了我们的主队列
主队列:GCD自带的一种特殊的串行队列

  • 所有放在主队列中的任务,都会放到主线程中执行
  • 可使用dispatch_get_main_queue()获得主队列
    So 我们还有两种了

1.主队列 + 同步执行
互等卡住不可行(在主线程中调用)

 NSLog(@"syncMain---begin");

    dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });   

    NSLog(@"syncMain---end");

输出

2017-06-07 17:04:12.154 GCD[20860:682212] syncMain---begin
(lldb) 

原因:我们把任务放在了主队列中,也就放在了主线程的队列中。同步执行就是对任务立马执行。把第一个任务放在了主队列中,立马执行,但是主队列还要继续往下走,主队列中第一个任务要执行,主队列却要继续执行第二个任务第三个,so就卡住了。

假设不在主线程中调用
不会开启新线程,执行完一个任务,再执行下一个任务(在其他线程中调用)

dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    [self syncMain];
});
syncMain---begin
1------<NSThread: 0x60000007ed80>{number = 1, name = main}
1------<NSThread: 0x60000007ed80>{number = 1, name = main}
2------<NSThread: 0x60000007ed80>{number = 1, name = main}
2------<NSThread: 0x60000007ed80>{number = 1, name = main}
3------<NSThread: 0x60000007ed80>{number = 1, name = main}
3------<NSThread: 0x60000007ed80>{number = 1, name = main}
syncMain---end
  • 在其他线程中使用主队列 +
    同步执行可看到:所有任务都是在主线程中执行的,并没有开启新的线程。而且由于主队列是串行队列,所以按顺序一个一个执行。
  • 同时我们还可以看到,所有任务都在打印的syncConcurrent—begin和syncConcurrent—end之间,这说明任务是添加到队列中马上执行的。

2. 主队列 + 异步执行
只在主线程中执行任务,执行完一个任务,再执行下一个任务

 NSLog(@"asyncMain---begin");

    dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });  

    NSLog(@"asyncMain---end");

输出

asyncMain---begin
asyncMain---end
1------<NSThread: 0x600000073dc0>{number = 1, name = main}
1------<NSThread: 0x600000073dc0>{number = 1, name = main}
2------<NSThread: 0x600000073dc0>{number = 1, name = main}
2------<NSThread: 0x600000073dc0>{number = 1, name = main}
3------<NSThread: 0x600000073dc0>{number = 1, name = main}
3------<NSThread: 0x600000073dc0>{number = 1, name = main}
  • 我们发现所有任务都在主线程中,虽然是异步执行,具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中,并且一个接一个执行
  • 所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。
    开发中大多数还是用的GCD,那就多说点

补充1: GCD的栅栏方法

假设你想异步执行两组操作,一组操作完才可以执行另一组,dispatch_barrier_async就应运而生了

 dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });

    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });

输出

----1-----<NSThread: 0x60000007e080>{number = 3, name = (null)}
----2-----<NSThread: 0x608000077e80>{number = 4, name = (null)}
----barrier-----<NSThread: 0x608000077e80>{number = 4, name = 
----4-----<NSThread: 0x60000007e080>{number = 3, name = (null)}
----3-----<NSThread: 0x608000077e80>{number = 4, name = (null)}

上面执行完,才执行下面的

补充2:GCD的延时执行方法

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒后异步执行这里的代码...
   NSLog(@"run-----");
});

补充3:GCD只执行一次

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次。

补充4:GCD快速迭代方法

和for循环遍历一样,GCD中是dispatch_apply,而且是同时遍历

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(6, queue, ^(size_t index) {
    NSLog(@"%zd------%@",index, [NSThread currentThread]);
});

输出

0------<NSThread: 0x600000077d40>{number = 1, name = main}
2------<NSThread: 0x6000002636c0>{number = 3, name = (null)}
3------<NSThread: 0x608000262380>{number = 5, name = (null)}
1------<NSThread: 0x608000262240>{number = 4, name = (null)}
4------<NSThread: 0x600000077d40>{number = 1, name = main}
5------<NSThread: 0x6000002636c0>{number = 3, name = (null)}

补充5: GCD的队列组

有多个耗时操作,全完成之后,再返回主线程,就要用到队列组
调用队列组的dispatch_group_notify回到主线程执行操作。

dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值