iOS多线程-关于GCD

Blog _ Oct 12, 2020

关于线程、队列、任务

无可厚非,大家都知道线程可分为:主线程子线程,在线程中,可以执行程序的一些任务。

在执行任务的时候,我个人认为任务是不能直接在线程中执行的,而是将每个任务添加到相应的队列中,线程去执行队列中的任务。

而队列又可分为串行队列和并行队列。

大致如下这样:

串行队列:一 一 一 一 一,一串单一任务逐步执行的队列。

并行队列:一二三二一三,一串多个任务同时进行的队列。

iOS中GCD的线程和队列

iOS中的主线程(main thread)中,默认有一条自己的串行主队列dispatch_get_main_queue() 。

在开发中可以将任务添加到默认的串行主队列中(注:这种添加只能在异步(async)执行中去进行,在同步[sync]执行中,会产生互相等待的死锁,因为同步执行,就是在主线程中去执行主队列的任务,执行完成后才会执行其他任务,这时函数本身就是主队列中的一个任务,而这时又在主队列中新增了一个任务,新增的任务会等待主队列执行完成,而同步执行又需要立刻执行,主队列又等待新任务的执行。),也可以将任务添加到系统全局并行队列中。

// 获取全局并行队列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


// 获取默认串行主队列
dispatch_queue_t main_queue = dispatch_get_main_queue();

// 同步执行(立刻执行任务)  // 这样会形成互相等待的死锁。
dispatch_sync(main_queue, ^{
    NSLog(@"执行任务 %@", [NSThread currentThread]);// 崩溃
});

当然,也可以自己创建队列,并添加任务。

// 创建并行队列
dispatch_queue_t con_queue = dispatch_queue_create("con_queue", DISPATCH_QUEUE_CONCURRENT);

// 创建串行队列
dispatch_queue_t se_queue = dispatch_queue_create("se_queue", DISPATCH_QUEUE_SERIAL);

自己创建队列会涉及到 线程同步执行队列 或 线程异步执行队列。

线程同步执行(dispatch_sync)队列,不创建子线程,直接在主线程中去执行。

同步执行+并行、串行队列

// 创建并行队列
dispatch_queue_t con_queue = dispatch_queue_create("con_queue", DISPATCH_QUEUE_CONCURRENT);

// 创建串行队列
dispatch_queue_t se_queue = dispatch_queue_create("se_queue", DISPATCH_QUEUE_SERIAL);

NSLog(@"任务0 %@",[NSThread currentThread]);

// sync同步执行->并行队列
dispatch_sync(con_queue, ^{
    sleep(2);// 睡2秒
    NSLog(@"任务1 %@", [NSThread currentThread]);
});
// sync同步执行->串行队列
dispatch_sync(se_queue, ^{
    sleep(1); // 睡1秒
    NSLog(@"任务2 %@", [NSThread currentThread]);
});

dispatch_queue_t g_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_sync(g_queue, ^{
    sleep(1);
    NSLog(@"任务3 %@", [NSThread currentThread]);
});

 NSLog(@"任务4 %@",[NSThread currentThread]);

执行结果:

2020-10-12 15:25:37.697483+0800 多线程[57501:1906376] 任务0 <NSThread: 0x600002484900>{number = 1, name = main}
2020-10-12 15:25:39.697805+0800 多线程[57501:1906376] 任务1 <NSThread: 0x600002484900>{number = 1, name = main}
2020-10-12 15:25:40.699222+0800 多线程[57501:1906376] 任务2 <NSThread: 0x600002484900>{number = 1, name = main}
2020-10-12 15:25:41.699739+0800 多线程[57501:1906376] 任务3 <NSThread: 0x600002484900>{number = 1, name = main}
2020-10-12 15:25:41.700039+0800 多线程[57501:1906376] 任务4 <NSThread: 0x600002484900>{number = 1, name = main}

可以看出,sync同步执行,无论是新建的并行还是串行队列,或者某个任务睡眠几秒,都是逐步从主线程中依次执行的。dispatch_sync不具备开启新的线程能力,所有任务都会依次在主线程中串行执行。

线程异步执行(dispatch_async)队列,可能会创建新的子线程,也可能会在主线程中执行,这主要取决于将任务添加到了默认串行主队列中,还是新建的队列中,默认串行主队列,会在主线程中依次串行执行,如果是新建的队列,则会根据串行或并行创建1个子线程或者并行多个子线程来执行。

异步执行 + 主队列

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


NSLog(@"任务1 %@",[NSThread currentThread]);
   
dispatch_async(main_queue, ^{
    sleep(2); // 睡眠2秒
    NSLog(@"任务2 %@",[NSThread currentThread]);
    
});
dispatch_async(main_queue, ^{
    NSLog(@"任务3 %@",[NSThread currentThread]);
    
});
sleep(5);
NSLog(@"任务4 %@",[NSThread currentThread]);

执行结果:

2020-10-12 16:53:12.390288+0800 多线程[58971:1978884] 任务1 <NSThread: 0x600000c40b40>{number = 1, name = main}
2020-10-12 16:53:17.391021+0800 多线程[58971:1978884] 任务4 <NSThread: 0x600000c40b40>{number = 1, name = main}
2020-10-12 16:53:19.404716+0800 多线程[58971:1978884] 任务2 <NSThread: 0x600000c40b40>{number = 1, name = main}
2020-10-12 16:53:19.404975+0800 多线程[58971:1978884] 任务3 <NSThread: 0x600000c40b40>{number = 1, name = main}

可以看出,所有任务全部都是从主线程main中执行的,首先主线程执行完主队列上的任务1和任务4,然后再一次去执行主队列上的2和3。整体仍是个串行队列。

异步执行+新建串行和并行队列

// 创建并行队列
dispatch_queue_t con_queue = dispatch_queue_create("con_queue", DISPATCH_QUEUE_CONCURRENT);

// 创建串行队列
dispatch_queue_t se_queue = dispatch_queue_create("se_queue", DISPATCH_QUEUE_SERIAL);

// 获取全局并行队列
dispatch_queue_t g_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 

NSLog(@"任务0 %@",[NSThread currentThread]);

// async异步执行->并行队列1
dispatch_async(con_queue, ^{
    sleep(1);// 睡2秒
    NSLog(@"任务并1 %@", [NSThread currentThread]);
});
// async异步执行->并行队列2
dispatch_async(con_queue, ^{
    sleep(2);// 睡2秒
    NSLog(@"任务并2 %@", [NSThread currentThread]);
});
// async异步执行->并行队列3
dispatch_async(con_queue, ^{
    sleep(3);// 睡2秒
    NSLog(@"任务并3 %@", [NSThread currentThread]);
});


// async异步执行->串行队列
dispatch_async(se_queue, ^{
    sleep(1);
    NSLog(@"任务串1 %@", [NSThread currentThread]);
});
dispatch_async(se_queue, ^{
    sleep(2);
    NSLog(@"任务串2 %@", [NSThread currentThread]);
});
dispatch_async(se_queue, ^{
    sleep(3);
    NSLog(@"任务串3 %@", [NSThread currentThread]);
});


dispatch_async(g_queue, ^{
    sleep(1);
    NSLog(@"任务全1 %@", [NSThread currentThread]);
});
dispatch_async(g_queue, ^{
    sleep(2);
    NSLog(@"任务全2 %@", [NSThread currentThread]);
});
dispatch_async(g_queue, ^{
    sleep(3);
    NSLog(@"任务全3 %@", [NSThread currentThread]);
});

sleep(5);

NSLog(@"任务2 %@",[NSThread currentThread]);

结果:

2020-10-12 17:12:26.162951+0800 多线程[59332:1996980] 任务0 <NSThread: 0x600003134cc0>{number = 1, name = main}
2020-10-12 17:12:27.165212+0800 多线程[59332:1997178] 任务并1 <NSThread: 0x60000317c780>{number = 6, name = (null)}
2020-10-12 17:12:27.165219+0800 多线程[59332:1997177] 任务串1 <NSThread: 0x60000316d700>{number = 5, name = (null)}
2020-10-12 17:12:27.165229+0800 多线程[59332:1997182] 任务全1 <NSThread: 0x60000317a340>{number = 3, name = (null)}
2020-10-12 17:12:28.165539+0800 多线程[59332:1997179] 任务并2 <NSThread: 0x600003170040>{number = 4, name = (null)}
2020-10-12 17:12:28.165566+0800 多线程[59332:1997184] 任务全2 <NSThread: 0x60000317a540>{number = 7, name = (null)}
2020-10-12 17:12:29.166544+0800 多线程[59332:1997177] 任务串2 <NSThread: 0x60000316d700>{number = 5, name = (null)}
2020-10-12 17:12:29.166556+0800 多线程[59332:1997180] 任务并3 <NSThread: 0x60000317ac00>{number = 8, name = (null)}
2020-10-12 17:12:29.166597+0800 多线程[59332:1997185] 任务全3 <NSThread: 0x600003164340>{number = 9, name = (null)}
2020-10-12 17:12:31.164223+0800 多线程[59332:1996980] 任务2 <NSThread: 0x600003134cc0>{number = 1, name = main}
2020-10-12 17:12:32.171168+0800 多线程[59332:1997177] 任务串3 <NSThread: 0x60000316d700>{number = 5, name = (null)}

从结果可以看出主线程中任务1和2,所有的任务串都是从线程5中依次执行的,而任务并和任务全,是开启了6条新线程(3/4/6/7/8/9)并发执行。

总结:

异步+并行时(全局并行或新建并行),会创建多个子线程去同时并发执行。

异步+串行时,只会开启一条子线程去依次执行串行队列。

异步+主队列(串行)时,不会开启新的线程,会在主线程执行完主队列的任务后,主线程再去执行新任务。

同步 + 串行或并行,无论新建的是串行或并行或全局并行,都是依次串行一个一个去执行。

同步+主队列,则会产生死锁。