iOS开发知识

《iOS高级编程》中的多线程与GCD

2019-08-10  本文已影响0人  太阳骑士索拉尔

关于我的仓库

前言

准备工作

什么是多线程以及GCD做了什么

多线程的定义

补充知识:进程与线程

补充知识:CPU,CPU核以及时间片

9E60A594-2ED9-433A-A4EB-74FB1DA49B10 20F74E5E-FF07-4CA2-88F2-B44077D834E7 687474703a2f2f7777342e73696e61696d672e636e2f6c617267652f303036744e6337396c7931673536633866667971766a33306d313066393075652e6a7067

多线程利弊

99A3CA0E-F480-4285-B549-C9F47285282B 08A8A176-481C-45F7-8552-58107F56D185

GCD中的API

Dispatch Queue

EC6A44D2-503A-4920-AF2C-DDCF5D865189 0C180D41-2CF4-450E-9209-6AA8E9CF2A20 8767B4F1-35CD-4850-A764-5BD3B20FEEBB

dispatch_queue_create

04A13A2C-EAA5-4A37-AD45-713B7395E6B9 331F2D9E-06BC-4883-87DE-33472A4D22CE

Main Dispatch Queue/Global Dispatch Queue

dispatch_set_target_queue

//首先创建5个串行队列
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.gcd.setTargetQueue2.serialQueue1", NULL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.gcd.setTargetQueue2.serialQueue2", NULL);
dispatch_queue_t serialQueue3 = dispatch_queue_create("com.gcd.setTargetQueue2.serialQueue3", NULL);
dispatch_queue_t serialQueue4 = dispatch_queue_create("com.gcd.setTargetQueue2.serialQueue4", NULL);
dispatch_queue_t serialQueue5 = dispatch_queue_create("com.gcd.setTargetQueue2.serialQueue5", NULL);

//每个队列上输出一个数字
dispatch_async(serialQueue1, ^{
    NSLog(@"1");
});
dispatch_async(serialQueue2, ^{
    NSLog(@"2");
});
dispatch_async(serialQueue3, ^{
    NSLog(@"3");
});
dispatch_async(serialQueue4, ^{
    NSLog(@"4");
});
dispatch_async(serialQueue5, ^{
    NSLog(@"5");
});

//这样就是5个串行队列在并行执行操作,执行结果无固定顺序
2017-02-28 21:32:48.787 GCDLearn[1449:71250] 5
2017-02-28 21:32:48.786 GCDLearn[1449:71242] 3
2017-02-28 21:32:48.786 GCDLearn[1449:71226] 1
2017-02-28 21:32:48.786 GCDLearn[1449:71235] 2
2017-02-28 21:32:48.786 GCDLearn[1449:71244] 4
  
//创建目标串行队列
dispatch_queue_t targetSerialQueue = dispatch_queue_create("com.gcd.setTargetQueue2.targetSerialQueue", NULL);

//设置执行阶层
dispatch_set_target_queue(serialQueue1, targetSerialQueue);
dispatch_set_target_queue(serialQueue2, targetSerialQueue);
dispatch_set_target_queue(serialQueue3, targetSerialQueue);
dispatch_set_target_queue(serialQueue4, targetSerialQueue);
dispatch_set_target_queue(serialQueue5, targetSerialQueue);

//执行操作
dispatch_async(serialQueue1, ^{
    NSLog(@"1");
});
dispatch_async(serialQueue2, ^{
    NSLog(@"2");
});
dispatch_async(serialQueue3, ^{
    NSLog(@"3");
});
dispatch_async(serialQueue4, ^{
    NSLog(@"4");
});
dispatch_async(serialQueue5, ^{
    NSLog(@"5");
});

//有序
2017-02-28 21:38:06.606 GCDLearn[1506:75803] 1
2017-02-28 21:38:06.607 GCDLearn[1506:75803] 2
2017-02-28 21:38:06.607 GCDLearn[1506:75803] 3
2017-02-28 21:38:06.608 GCDLearn[1506:75803] 4
2017-02-28 21:38:06.608 GCDLearn[1506:75803] 5

dispatch_after

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);

// 在3秒后追加Block到Main Dispatch Queue中
dispatch_after(time, dispatch_get_main_queue(), ^{
    
    NSLog(@"waited at least three seconds");;
});

dispatch_after函数
第一个参数:
指定时间的dispatch_time_t类型的值,可以使用dispatch_time函数或dispatch_walltime函数作成。

第二个参数:
要追加处理的Dispatch Queue。

第三个参数:
要执行处理的Block。
// 从现在开始1秒后的dispatch_time_t类型的值
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);

“ull”是C语言的数值字面量,是显示表明类型时使用的字符串。
NSEC_PER_SEC单位:毫微秒
NSEC_PER_MSEC单位:毫秒

Dispatch Group

// 例子:追加3个Block到Global Dispatch Queue,这些处理全部执行完毕,会执行追加到Main Dispatch Queue中的结束处理Block

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    NSLog(@"0");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"1");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"2");
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"done");
});

dispatch_release(group);

说明:
dispatch_group_create函数生成dispatch_group_t类型的Dispatch Group,函数中含有create,所以在使用结束后需要通过dispatch_release函数释放。

dispatch_group_async函数,第一个参数为生成的Dispatch Group,第二个参数为Dispatch Queue,第三个参数是Block,将Block追加到Dispatch Queue中,Block属于指定的Dispatch Group。

Block通过dispatch_retain函数持有Dispatch Group,Block执行结束,通过dispatch_release函数释放持有的Dispatch Group。Dispatch Group使用结束,不用考虑Block,立即通过dispatch_release函数释放即可。

dispatch_group_notify函数第一个参数为要监视的Dispatch Group,第二个参数为要追加结束处理的Dispatch Queue,第三个结束处理Block。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    NSLog(@"0");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"1");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"2");
});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

说明:
dispatch_group_wait函数第一个参数为等待的Dispatch Group,第二个参数为等待的时间(超时),为dispatch_time_t类型。
DISPATCH_TIME_FOREVER:永久等待。
使用forever永久等待,只要group里的处理没有结束,就会一直等待,中途不能取消
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);

long result = dispatch_group_wait(group, time);

if (result == 0) {
    // 全部处理执行结束
} else {
    // 某一个处理还在执行中
}

说明:
dispatch_group_wait函数返回值为0:全部处理执行结束
dispatch_group_wait函数返回值不为0:某一个处理还在执行中
等待时间为DISPATCH_TIME_FOREVER,返回值恒为0。
注意:一旦调用dispatch_group_wait函数,该函数就处于调用状态而不返回。执行该函数的当前线程停止,在经过该函数中指定的时间或该函数的所有处理全部执行结束之前,执行该函数的线程停止。
  
//  指定DISPATCH_TIME_NOW,则不用任何等待即可判断属于Dispatch Group的处理是否全部执行结束
long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);

dispatch_barrier_async

dispatch_queue_t queue = dispatch_queue_create("com.example.gdc.ForBarrier", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"reading1");
});

dispatch_async(queue, ^{
    NSLog(@"reading2");
});

dispatch_async(queue, ^{
    NSLog(@"reading3");
});

dispatch_barrier_async(queue, ^{
    NSLog(@"writing");
});

dispatch_async(queue, ^{
    NSLog(@"reading4");
});

dispatch_async(queue, ^{
    NSLog(@"reading5");
});

输出:
2018-07-23 22:38:16.498997+0800 Demo[9100:359307] reading3
2018-07-23 22:38:16.498997+0800 Demo[9100:359308] reading1
2018-07-23 22:38:16.498997+0800 Demo[9100:359311] reading2
2018-07-23 22:38:16.500972+0800 Demo[9100:359308] writing
2018-07-23 22:38:16.501812+0800 Demo[9100:359308] reading4
2018-07-23 22:38:16.501831+0800 Demo[9100:359311] reading5

dispatch_sync

死锁

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
    NSLog(@"处理");
});
死锁的产生
dispatch_queue_t queue = dispatch_queue_create("com.example.gdc.mySerialDispatchQueue", NULL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{
        NSLog(@"处理");
    });
});

//这个例本质上也是一样的

dispatch_apply

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zu",index);
});
NSLog(@"done");

输出:
2018-07-24 23:27:57.588905+0800 Demo[14916:512488] 0
2018-07-24 23:27:57.588909+0800 Demo[14916:512562] 3
2018-07-24 23:27:57.588905+0800 Demo[14916:512565] 2
2018-07-24 23:27:57.588906+0800 Demo[14916:512563] 1
2018-07-24 23:27:57.589128+0800 Demo[14916:512562] 4
2018-07-24 23:27:57.589128+0800 Demo[14916:512488] 5
2018-07-24 23:27:57.589132+0800 Demo[14916:512565] 6
2018-07-24 23:27:57.589220+0800 Demo[14916:512563] 7
2018-07-24 23:27:57.589284+0800 Demo[14916:512562] 8
2018-07-24 23:27:57.589311+0800 Demo[14916:512488] 9
2018-07-24 23:27:57.590148+0800 Demo[14916:512488] done

第一个参数:重复次数
第二个参数:执行处理的Diapatch Queue
第三个参数:要执行的处理,带有参数,第多少次,是为了按第一个参数重复追加Block并区分各个Block而使用的。

在Global Dispatch Queue中并行执行,全部处理都会执行,各个处理执行时间不定,。

dispatch_suspend/dispatch_resume

Dispatch Semaphore

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

NSMutableArray *array = @[].mutableCopy;

for (int i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        [array addObject:[NSNumber numberWithInt:i]];
    });
}

说明:可能出现同时访问数组,造成异常结束的问题。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

说明:
dispatch_semaphore_create函数生成Dispatch Semaphore。
参数表示计数的初始值。
含有create可以看出,该函数与Dispatch Queue和Dispatch Group一样,必须通过dispatch_release函数释放,也可以通过dispatch_retain函数持有。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

说明:
该函数等待第一个参数semaphore的计数值达到大于等于1。
当计数值大于等于1,或者在待机(等待的时间)中计数值大于等于1,对该计数进行减1并从该函数返回。
第二个参数是dispatch_time_t类型值,与dispatch_group_wait函数相同。
DISPATCH_TIME_FOREVER:永久等待。

dispatch_semaphore_signal(semaphore);

说明:
处理结束时通过该函数将semaphore的计数值加1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);

long result = dispatch_semaphore_wait(semaphore, time);

if (result == 0) {
    // 由于semaphore的计数值大于等于1或在等待时间内计数值大于等于1, 所以semaphore的计数值减1 执行需要进行排他控制的处理
} else {
    // semaphore的计数值直到1秒的等待时间结束都为0。
}

说明:
dispatch_semaphore_wait函数的返回值与dispatch_group_wait函数相同,semaphore大于等于1,result为0,semaphore为0,result不为0。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

NSMutableArray *array = @[].mutableCopy;

for (int i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        [array addObject:[NSNumber numberWithInt:i]];
        
        // 处理结束时通过该函数将semaphore的计数值加1
        dispatch_semaphore_signal(semaphore);
    });
}

// 使用结束,释放semaphore
dispatch_release(semaphore);

dispatch_once

static int initialized = NO;
if (initialized == NO) {
    // 初始化
    initialized = YES;
}

//说明:
//上面代码,在大多数情况下是安全的,但是在多核CPU中,在正在更新表示是否初始化的标志变量initialized时读取,就有可能多次执行初始化处理
static dispatch_once_t pred;
dispatch_once(&pred, ^{
    //初始化
});

//说明:
//在多线程环境下执行,也可以保证安全

Dispatch I/O

Dispatch I/O和Dispatch Data可以实现输入/输出做到多个线程并列读取。
Dispatch I/O读写文件时,使用Global Dispatch Queue将一个文件按某个大小read/write。
分割读取的数据使用Dispatch Data可以更为简单的进行结合和分割。

// Apple System Log API用的源代码
static int
_asl_auxiliary(aslmsg msg, const char *title, const char *uti, const char *url, int *out_fd)
{
    asl_msg_t *merged_msg;
    asl_msg_aux_t aux;
    asl_msg_aux_0_t aux0;
    fileport_t fileport;
    kern_return_t kstatus;
    uint32_t outlen, newurllen, len, where;
    int status, fd, fdpair[2];
    caddr_t out, newurl;
    dispatch_queue_t pipe_q;
    dispatch_io_t pipe_channel;
    dispatch_semaphore_t sem;
    /* ..... 此处省略若干代码.....*/
    
    // 创建串行队列
    pipe_q = dispatch_queue_create("PipeQ", NULL);
    // 生成Dispatch I/O,指定发生错误时执行处理的Block,以及执行该Block的Dispatch Queue。 
    pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){
        close(fd);
    });
    
    *out_fd = fdpair[1];
    
    // 该函数设定一次读取的大小(分割大小)
    dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
    // 使用Global Dispatch Queue并列读取,当每个分割的文件块读取结束,将Dispatch Data传递给回调的Block.
    dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){
        if (err == 0) // err等于0 说明读取无误
        {
            // 读取完“单个文件块”的大小
            size_t len = dispatch_data_get_size(pipedata);
            if (len > 0)
            {
                // 定义一个字节数组bytes
                const char *bytes = NULL;
                char *encoded;
                
                dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
                encoded = asl_core_encode_buffer(bytes, len);
                asl_set((aslmsg)merged_msg, ASL_KEY_AUX_DATA, encoded);
                free(encoded);
                _asl_send_message(NULL, merged_msg, -1, NULL);
                asl_msg_release(merged_msg);
                dispatch_release(md);
            }
        }
        
        if (done)
        {
            dispatch_semaphore_signal(sem);
            dispatch_release(pipe_channel);
            dispatch_release(pipe_q);
        }
    });
// 异步串行读取文件
NSString *desktop = @"/Users/xxxx/Desktop";
NSString *path = [desktop stringByAppendingPathComponent:@"整理.md"];
dispatch_queue_t queue = dispatch_queue_create("queue", NULL);

dispatch_fd_t fd = open(path.UTF8String, O_RDONLY, 0);
dispatch_io_t io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
    close(fd);
});

size_t water = 1024 * 1024;
dispatch_io_set_low_water(io, water);
dispatch_io_set_high_water(io, water);

long long fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil].fileSize;
NSMutableData *totalData = [[NSMutableData alloc] init];

dispatch_io_read(io, 0, fileSize, queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
    
    if (error == 0) {
        size_t len = dispatch_data_get_size(data);
        if (len > 0) {
            [totalData appendData:(NSData *)data];
        }
    }
    
    if (done) {
        NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding];
        NSLog(@"%@", str);
    }
});
// 异步并行读取文件
NSString *desktop = @"/Users/xxx/Desktop";
NSString *path = [desktop stringByAppendingPathComponent:@"整理.md"];

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_fd_t fd = open(path.UTF8String, O_RDONLY);
dispatch_io_t io = dispatch_io_create(DISPATCH_IO_RANDOM, fd, queue, ^(int error) {
    close(fd);
});

off_t currentSize = 0;
long long fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil].fileSize;

size_t offset = 1024 * 1024;
dispatch_group_t group = dispatch_group_create();
NSMutableData *totalData = [[NSMutableData alloc] initWithLength:fileSize];

for (; currentSize <= fileSize; currentSize += offset) {
    dispatch_group_enter(group);
    dispatch_io_read(io, currentSize, offset, queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        
        if (error == 0) {
            size_t len = dispatch_data_get_size(data);
            if (len > 0) {
                const void *bytes = NULL;
                (void)dispatch_data_create_map(data, (const void **)&bytes, &len);
                [totalData replaceBytesInRange:NSMakeRange(currentSize, len) withBytes:bytes length:len];
            }
        }
        
        if (done) {
            dispatch_group_leave(group);
            
            NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding];
            NSLog(@"%@", str);
        }
    });
}
上一篇 下一篇

猜你喜欢

热点阅读