GCD的概念

GCD(Grand Central Dispatch),是有Apple公司开发的一个多核编程的解决方案,用以优化应用程序支持多核处理器,是基于线程模式之上执行并发任务。

GCD的优点

1.利用设备多核进行并行运算
2.GCD自动充分使用设备的CPU内核
3.GCD自动管理线程的生命周期(线程创建、线程调度、线程销毁)
4.使用简单

GCD任务和队列

任务:就是执行操作,即可以执行的代码;执行任务有两种方式:同步异步

  • 同步(sync)
    阻塞线程: 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行;
    不可以开辟新的线程
  • 异步(async)
    不会阻塞线程:异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务
    可以开辟新的线程

队列Dispatch Queue指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,遵循 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务;

在 GCD 中有两种队列:串行队列并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。

  • 串行队列(Serial Dispatch Queue): 每次只有一个任务被执行。让任务一个接着一个地执行

  • 并发队列(Concurrent Dispatch Queue): 可以让多个任务同时执行。(可以开启多个线程,并且同时执行任务)

GCD 的使用步骤

GCD的使用分为两步:

1. 创建队列
2. 将任务追加到队列中

创建队列

创建队列使用dispatch_queue_create函数

   /*创建队列label:队列的唯一标识符attr:用来识别是串行队列还是并发队列;DISPATCH_QUEUE_SERIAL 表示串行队列;DISPATCH_QUEUE_CONCURRENT 表示并发队列;dispatch_queue_tdispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);*/dispatch_queue_t queue = dispatch_queue_create("qctt", DISPATCH_QUEUE_SERIAL);

获取队列

串行队列,GCD 默认提供了主队列(Main Dispatch Queue

 //获取主队列dispatch_queue_t mainqueue = dispatch_get_main_queue();

并发队列,GCD 默认提供了全局并发队列(Global Dispatch Queue

 /**参数一表示队列优先级,默认 DISPATCH_QUEUE_PRIORITY_DEFAULTDISPATCH_QUEUE_PRIORITY_DEFAULTDISPATCH_QUEUE_PRIORITY_HIGHDISPATCH_QUEUE_PRIORITY_DEFAULTDISPATCH_QUEUE_PRIORITY_LOWDISPATCH_QUEUE_PRIORITY_BACKGROUND参数二暂时没用,用 0 即可*/dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

创建任务

GCD提供了创建同步任务以及异步任务的函数;
同步任务: dispatch_sync

 // 同步执行任务创建方法dispatch_sync(dispatch_get_global_queue(0, 0), ^{NSLog(@"%s",__func__);});

异步任务: dispatch_async

  // 异步执行任务创建方法dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"%s",__func__);});

任务和队列不同组合方式的区别

区别 串行队列 并发队列 主队列
sync 没有开启新线程,串行 没有开启新线程,串行 死锁卡住不执行
async 开启新线程,串行 开启新线程,并发 没有开启新线程,串行

GCD 的组合使用

串行队列 + 同步执行

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

  /*** 串行队列 + 同步执行* 特点:不会开启新线程,在当前线程执行任务。执行完一个任务,再执行下一个任务。*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);dispatch_sync(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"syncSerial---end");

运行结果同步顺序执行

串行队列 + 异步执行

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

 /*** 串行队列 + 异步执行* 特点:会开启新线程。执行完一个任务,再执行下一个任务。*/dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{// 任务 1[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 3[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"asyncSerial---end");

打印日志:

2022-05-31 10:20:28.125523+0800 DemoTest2022[5271:199261] asyncSerial---end
2022-05-31 10:20:29.125730+0800 DemoTest2022[5271:199349] 1---<NSThread: 0x600003d8fb80>{number = 5, name = (null)}
2022-05-31 10:20:32.131137+0800 DemoTest2022[5271:199349] 2---<NSThread: 0x600003d8fb80>{number = 5, name = (null)}
2022-05-31 10:20:34.132492+0800 DemoTest2022[5271:199349] 3---<NSThread: 0x600003d8fb80>{number = 5, name = (null)}

并发队列 + 同步执行

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

  /*** 并发队列 + 同步执行* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_sync(queue, ^{// 任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 任务 3[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"syncConcurrent---end");

2022-05-31 10:31:42.719133+0800 DemoTest2022[5476:209855] 1---<_NSMainThread: 0x6000032507c0>{number = 1, name = main}
2022-05-31 10:31:45.719679+0800 DemoTest2022[5476:209855] 2---<_NSMainThread: 0x6000032507c0>{number = 1, name = main}
2022-05-31 10:31:46.720250+0800 DemoTest2022[5476:209855] 3---<_NSMainThread: 0x6000032507c0>{number = 1, name = main}
2022-05-31 10:31:46.720599+0800 DemoTest2022[5476:209855] syncConcurrent---end

并发队列 + 异步执行

可以开启多个线程,任务同时执行。

   /*** 并发队列 + 异步执行* 特点:可以开启多个线程,任务同时执行。*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{// 任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 3[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"asyncConcurrent---end");

执行结果:

2022-05-31 10:27:26.350321+0800 DemoTest2022[5398:206005] asyncConcurrent---end
2022-05-31 10:27:27.351422+0800 DemoTest2022[5398:206201] 3---<NSThread: 0x600002d22dc0>{number = 4, name = (null)}
2022-05-31 10:27:28.354696+0800 DemoTest2022[5398:206202] 1---<NSThread: 0x600002d3c7c0>{number = 6, name = (null)}
2022-05-31 10:27:29.352304+0800 DemoTest2022[5398:206200] 2---<NSThread: 0x600002d34e00>{number = 9, name = (null)}

GCD 的其他方法栅栏函数

GCD栅栏方法: dispatch_barrier_async

dispatch_barrier_async 方法会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在 dispatch_barrier_async 方法追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列并开始执行。

void
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
   /*** 栅栏方法 dispatch_barrier_async*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_barrier_async(queue, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程});dispatch_async(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 追加任务 4[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程});

执行日志:先执行栅栏之前的21,然后执行栅栏,再添加后续任务并执行

2022-05-31 10:48:48.606711+0800 DemoTest2022[5722:224160] 2---<NSThread: 0x600002b12b80>{number = 8, name = (null)}
2022-05-31 10:48:49.607101+0800 DemoTest2022[5722:224161] 1---<NSThread: 0x600002b05d00>{number = 5, name = (null)}
2022-05-31 10:48:51.608045+0800 DemoTest2022[5722:224161] barrier---<NSThread: 0x600002b05d00>{number = 5, name = (null)}
2022-05-31 10:48:52.609360+0800 DemoTest2022[5722:224161] 3---<NSThread: 0x600002b05d00>{number = 5, name = (null)}
2022-05-31 10:48:53.608776+0800 DemoTest2022[5722:224165] 4---<NSThread: 0x600002b0f900>{number = 7, name = (null)}

栅栏函数实现多读单写

#import "UserCenter.h"@interface UserCenter()
{// 定义一个并发队列dispatch_queue_t concurrent_queue;// 用户数据中心, 可能多个线程需要数据访问NSMutableDictionary *userCenterDic;
}@end// 多读单写模型
@implementation UserCenter- (id)init
{self = [super init];if (self) {// 通过宏定义 DISPATCH_QUEUE_CONCURRENT 创建一个并发队列concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);// 创建数据容器userCenterDic = [NSMutableDictionary dictionary];}return self;
}- (id)objectForKey:(NSString *)key
{__block id obj;// 同步读取指定数据dispatch_sync(concurrent_queue, ^{obj = [userCenterDic objectForKey:key];});return obj;
}- (void)setObject:(id)obj forKey:(NSString *)key
{// 异步栅栏调用设置数据dispatch_barrier_async(concurrent_queue, ^{[userCenterDic setObject:obj forKey:key];});
}@end

GCD 延时执行方法:dispatch_after

dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。

   /*** 延时执行方法 dispatch_after*/dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2.0 秒后异步追加任务代码到主队列,并开始执行NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程});
}

GCD 执行一次:dispatch_once

在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的 dispatch_once 方法。使用 dispatch_once 方法能保证某段代码在程序运行过程中只被执行 1 次,并且即使在多线程的环境下,dispatch_once 也可以保证线程安全。

void
dispatch_once(dispatch_once_t *predicate,DISPATCH_NOESCAPE dispatch_block_t block);
/*** 只执行一次 dispatch_once*/
static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// 只执行 1 次的代码(这里面默认是线程安全的)
});

GCD 队列组:dispatch_group

场景:当需要异步执行多个耗时任务,然后执行完毕后回到主线程;

  • dispatch_group_notify: 监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务;

      dispatch_group_t group = dispatch_group_create();dispatch_queue_t global = dispatch_get_global_queue(0, 0);dispatch_group_async(group, global, ^{//添加任务[NSThread sleepForTimeInterval:2];NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程dispatch_async(global, ^{[NSThread sleepForTimeInterval:1];NSLog(@"11111---%@",[NSThread currentThread]);});});dispatch_group_async(group, global, ^{//添加任务[NSThread sleepForTimeInterval:1];NSLog(@"2---%@",[NSThread currentThread]);dispatch_async(global, ^{[NSThread sleepForTimeInterval:2];NSLog(@"222222---%@",[NSThread currentThread]);});});dispatch_group_notify(group, dispatch_get_main_queue(), ^{//等待 1,2 任务执行完成后到主线程执行3[NSThread sleepForTimeInterval:1];NSLog(@"3---%@",[NSThread currentThread]);NSLog(@"group end");});
    

    2022-05-31 14:12:15.717237+0800 DemoTest2022[8494:370194] 2---<NSThread: 0x60000224d940>{number = 9, name = (null)}
    2022-05-31 14:12:16.717106+0800 DemoTest2022[8494:370195] 1---<NSThread: 0x600002230980>{number = 4, name = (null)}
    2022-05-31 14:12:17.718496+0800 DemoTest2022[8494:369922] 3---<_NSMainThread: 0x600002228ac0>{number = 1, name = main}
    2022-05-31 14:12:17.718496+0800 DemoTest2022[8494:370195] 11111---<NSThread: 0x600002230980>{number = 4, name = (null)}
    2022-05-31 14:12:17.718496+0800 DemoTest2022[8494:370194] 222222---<NSThread: 0x60000224d940>{number = 9, name = (null)}
    2022-05-31 14:12:17.718751+0800 DemoTest2022[8494:369922] group end

  • dispatch_group_wait: 暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行
       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 , ^{// 追加任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_group_async(group, queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(@"group---end");

    2022-05-31 14:36:09.722970+0800 DemoTest2022[8888:390059] 1---<NSThread: 0x6000027da3c0>{number = 3, name = (null)}
    2022-05-31 14:36:09.722967+0800 DemoTest2022[8888:390062] 1---<NSThread: 0x6000027de940>{number = 7, name = (null)}
    2022-05-31 14:36:09.723212+0800 DemoTest2022[8888:389913] group---end

  • dispatch_group_enter: 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1; 
    void
    dispatch_group_enter(dispatch_group_t group);
    
  • dispatch_group_leave: 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1;

    void
    dispatch_group_leave(dispatch_group_t group);
    

    当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务;

    /*** 队列组 dispatch_group_enter、dispatch_group_leave*/
    - (void)groupEnterAndLeave {NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程NSLog(@"group---begin");dispatch_group_t group = dispatch_group_create();dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_enter(group);dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程dispatch_group_leave(group);});dispatch_group_enter(group);dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程dispatch_group_leave(group);});dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的异步操作都执行完毕后,回到主线程.[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程NSLog(@"group---end");});
    }
    

    2022-05-31 14:56:53.413320+0800 DemoTest2022[9165:405241] currentThread---<_NSMainThread: 0x600001024500>{number = 1, name = main}
    2022-05-31 14:56:53.413467+0800 DemoTest2022[9165:405241] group---begin
    2022-05-31 14:56:55.415122+0800 DemoTest2022[9165:405400] 1---<NSThread: 0x60000102e080>{number = 6, name = (null)}
    2022-05-31 14:56:55.415122+0800 DemoTest2022[9165:405398] 2---<NSThread: 0x600001078040>{number = 8, name = (null)}
    2022-05-31 14:56:57.415797+0800 DemoTest2022[9165:405241] 3---<_NSMainThread: 0x600001024500>{number = 1, name = main}
    2022-05-31 14:56:57.416083+0800 DemoTest2022[9165:405241] group---end

GCD 信号量:dispatch_semaphore

信号量是基于计数器的一种多线程同步机制,用来管理对资源的并发访问。
GCD 中的信号量是指持有计数的信号。在 Dispatch Semaphore 中,使用计数来完成这个功能,信号量为0则阻塞线程,大于0则不会阻塞。则我们通过改变信号量的值,来控制是否阻塞线程,从而达到线程同步。
Dispatch Semaphore 提供了三个方法:

  • dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量
  • dispatch_semaphore_signal:发送一个信号,让信号总量加 1
  • dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。

信号量dispatch_semaphore主要用于两个方面:保持线程同步、为线程加锁

  • 保持线程同步

/** 以AFNetworking源码为例*/ 
  • 为线程加锁

     dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);for (int i = 0; i < 10; i++) {dispatch_async(queue, ^{// 相当于加锁 -1操作dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);/*todo:*/// 相当于解锁 +1操作。 计数器>= 0就不阻塞线程int t = rand()%3;//随机0-3秒休眠耗时操作[NSThread sleepForTimeInterval:t];NSLog(@"%d",i);dispatch_semaphore_signal(semaphore);});}

    2022-05-31 17:18:02.966335+0800 DemoTest2022[11107:503565] 0
    2022-05-31 17:18:03.970640+0800 DemoTest2022[11107:503570] 1
    2022-05-31 17:18:05.972503+0800 DemoTest2022[11107:503564] 2
    2022-05-31 17:18:07.976567+0800 DemoTest2022[11107:503566] 3
    2022-05-31 17:18:08.980964+0800 DemoTest2022[11107:503569] 4
    2022-05-31 17:18:10.984335+0800 DemoTest2022[11107:503568] 5
    2022-05-31 17:18:10.984578+0800 DemoTest2022[11107:503572] 6
    2022-05-31 17:18:12.985732+0800 DemoTest2022[11107:503573] 7
    2022-05-31 17:18:14.991233+0800 DemoTest2022[11107:503574] 8
    2022-05-31 17:18:15.995794+0800 DemoTest2022[11107:503575] 9

iOS多线程之GCD相关推荐

  1. iOS多线程之GCD小记

    iOS多线程之GCD小记 iOS多线程方案简介 从各种资料中了解到,iOS中目前有4套多线程的方案,分别是下列4中: 1.Pthreads 这是一套可以在很多操作系统上通用的多线程API,是基于C语言 ...

  2. (五十五)iOS多线程之GCD

    GCD的全称为Grand Central Dispatch,翻译为大中央调度,是Apple开发的一个多线程编程解决方法. 进程和线程的概念: 正在进行中的程序被称为进程,负责程序运行的内存分配,每一个 ...

  3. IOS中的多线程之GCD

    在ios中,使用多线程有三种方式,分别是:NSThread.NSOperation和NSOperationQueue.GCD,在本节,主要讲解一下CDD的使用. GCD(Grand Central D ...

  4. iOS 线程之GCD的高级使用方法

    之前的一篇关于线程的blog已经为大家介绍了GCD的简单使用方式及样例说明,今天因为项目中有特殊的应用GCD的实例,为大家介绍两种特殊需求的使用GCD的方法. 目的:实现一件事情做完,再做下一件事情. ...

  5. IOS多线程之Block编程

    1 什么是block iOS SDK 4.0開始,Apple引入了block这一特性.字面上说,block就是一个代码块.可是它的奇妙之处在于在内联(inline)运行的时候(这和C++非常像)还能够 ...

  6. iOS多线程之7.NSOperation的初识

    NSOperation和GCD一样,不用我们管理线程的生命周期,加锁等问题,只要把操作封装进NSOperation中,系统会自动帮我们创建线程,执行操作.而且他是面向对象的,我们看起来更容易理解,使用 ...

  7. (五十六)iOS多线程之NSOperation

    NSOpertation是一套OC的API,是对GCD进行的Cocoa抽象. NSOperation有两种不同类型的队列,主队列和自定义队列. 主队列运行于主线程上,自定义队列在后台运行. [NSBl ...

  8. 关于多线程之GCD的一些学习要点

    GCD是当前多线程使用最方便的,也是使用比较多的. 学习GCD主要集中在一下几点: 一.队列,同步,异步 1.主队列:dispatch_get_main_queue(); 2.串行队列:dispatc ...

  9. ios多线程之NSThread头文件详解

    1.NSThread 头文件中的相关方法 //获取当前线程+(NSThread *)currentThread; //创建线程后自动启动线程 + (void)detachNewThreadSelect ...

  10. linux 线程pthread_detach,linux线程之pthread_join和pthread_detach

    在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死.在 被其他线程回收之前,它的存储器资源(例如栈)是不释放的.相反 ...

最新文章

  1. Maven的生命周期和maven-assembly-plugin插件使用
  2. 万变不离其宗之海量数据下的算法问题处理思路
  3. 图形—9patch,shape ,selector
  4. TreeView无限极分类绑定(从数据库读取数据源)
  5. 让linux的SSH客户端也能记住服务器的密码
  6. 快递员遭投诉吞安眠药护自尊 顺丰王卫:马上检讨 立即整改
  7. java多条件不定条件查询
  8. Atitit.request http乱码的设计防止 检测与解决最近实践p825 attilax总结.doc
  9. 微信小程序的剪贴板 +复制剪贴,在淘宝中打开就可以获取到商品
  10. 计算机房防凝露保温材料,机房保温的方案.docx
  11. 银企直连ukey 集中管理方案
  12. java 游戏源码_源码下载 - Java源码 - 游戏娱乐
  13. 计算机二级ppt文小雨,计算机二级PPT真题:制作日月潭介绍PPT
  14. 虚拟机内linux网络连接,vmware中redhat5虚拟机无法连接网络
  15. 数字黑洞java_java实现数字黑洞
  16. sping循环依赖:but has eventually been wrapped. This means that said other beans do not use the final
  17. Axure计算器原型
  18. 从头来过教你PHP脚本语言(先导篇)
  19. cdn连接失败是什么意思_CDN经常连接失败的原因有哪些?
  20. 全新安装Windows10系统(PE下)

热门文章

  1. 大数据七大岗位及职责
  2. 狂神说-Spring学习总结
  3. java duplicate key_java.lang.IllegalStateException: Duplicate key 1
  4. Centos 8 安装 Openbravo 之安装 httpd mod_jk
  5. JQuery 简单表单验证
  6. 技巧推荐 PDF文件怎么转成word文档
  7. 【随笔】那些免费友好的遥感影像数据下载网站
  8. STM32学习笔记——USB鼠标
  9. 香港长和集团开创P2F金融理财新征程
  10. Boot Option中没有USB启动选项的解决办法