美文网首页
iOS多线程

iOS多线程

作者: 芮小妹儿很闲 | 来源:发表于2018-05-22 16:33 被阅读9次

    整理下几种常用的处理多线程的类,并将该类对应的常用方法整理下,采用简单直观的例子展示。

    1、NSThread

    NSThread的创建方法有三种:

    • init方法,需要start启动
    • detachNewThreadSelector方法,自动启动
    • performSelectorInBackground方法,自动启动

    init方法

    NSThread *threadInit = [[NSThread alloc] initWithTarget:self selector:@selector(thread_initMethod:) object:@"initMethod"];
    [threadInit start];
    
    - (void)thread_initMethod:(NSObject *)object
    {
        NSLog(@"init方法 --> object:%@, %@",object, [NSThread currentThread]);
    }
    

    输出结果:

    init方法 --> object:initMethod, <NSThread: 0x60000027b180>{number = 3, name = (null)}

    是在子线程中完成。

    detachNewThreadSelector方法

    [NSThread detachNewThreadSelector:@selector(thread_detch:) toTarget:self withObject:@"detachNewThread"];
    
    - (void)thread_detch:(NSObject *)object
    {
        NSLog(@"detach方法 --> object:%@, %@",object, [NSThread currentThread]);
    }
    

    输出结果:

    detach方法 --> object:detachNewThread, <NSThread: 0x60000027b0c0>{number = 4, name = (null)}

    是在子线程中完成。

    performSelectorInBackground方法

    [self performSelectorInBackground:@selector(thread_perform:) withObject:@"performSelectorInBackground"];
    
    - (void)thread_perform:(NSObject *)object
    {
        NSLog(@"perform方法--> object:%@, %@",object, [NSThread currentThread]);
    }
    

    输出结果:

    perform方法--> object:performSelectorInBackground, <NSThread: 0x60000007a2c0>{number = 5, name = (null)}

    是在子线程中完成。

    2、GCD

    GCD中的基本概念:

    • 任务:执行的代码块
    • 队列:存放任务的地方
    • 同步/异步:执行的方式

    GCD中的三种队列类型:

    • The main queue:与主线程功能相同。实际上,提交至main queue(主队列)的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
    • Global queues:全局队列是并发队列,并由整个进程共享。进程中存在四个全局队列:高、中(默认)、低、后台四个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
    • 其他队列: 其他队列是用函数dispatch_queue_create创建的队列。创建队列,第一个参数是表示debug用的,Apple建议我们使用倒置域名来命名队列;第二个参数表示队列类型:串行(DISPATCH_QUEUE_SERIAL)或者并发(DISPATCH_QUEUE_CONCURRENT)。

    串行同步

    dispatch_queue_t serialQueue = dispatch_queue_create("com.multithreading.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"串行同步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"串行同步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_sync(serialQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"串行同步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    

    输出结果:

    串行同步:0 --> <NSThread: 0x600000072d40>{number = 1, name = main}
    串行同步:1 --> <NSThread: 0x600000072d40>{number = 1, name = main}
    串行同步:2 --> <NSThread: 0x600000072d40>{number = 1, name = main}
    串行同步:0 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
    串行同步:1 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
    串行同步:2 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
    串行同步:0 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
    串行同步:1 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
    串行同步:2 --> <NSThread: 0x60400007ee80>{number = 1, name = main}

    都是在主线程中完成,没有开辟新线程。

    串行异步

    dispatch_queue_t serialQueue = dispatch_queue_create("com.multithreading.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"串行异步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"串行异步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"串行异步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    

    输出结果:

    串行异步:0 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:1 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:2 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:0 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:1 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:2 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:0 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:1 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
    串行异步:2 --> <NSThread: 0x604000467900>{number = 3, name = (null)}

    开辟了一个新线程。

    并发同步

    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.multithreading.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"并发同步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"并发同步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"并发同步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    

    输出结果:

    并发同步:0 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:1 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:2 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:0 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:1 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:2 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:0 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:1 --> <NSThread: 0x60000007c980>{number = 1, name = main}
    并发同步:2 --> <NSThread: 0x60000007c980>{number = 1, name = main}

    在主线程中完成,没有开辟新线程。

    并发异步

    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.multithreading.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"并发异步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"并发异步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"并发异步:%i --> %@",i, [NSThread currentThread]);
        }
    });
    

    输出结果:

    并发异步:0 --> <NSThread: 0x600000277480>{number = 4, name = (null)}
    并发异步:0 --> <NSThread: 0x604000465f80>{number = 3, name = (null)}
    并发异步:0 --> <NSThread: 0x604000466140>{number = 5, name = (null)}
    并发异步:1 --> <NSThread: 0x600000277480>{number = 4, name = (null)}
    并发异步:1 --> <NSThread: 0x604000465f80>{number = 3, name = (null)}
    并发异步:1 --> <NSThread: 0x604000466140>{number = 5, name = (null)}
    并发异步:2 --> <NSThread: 0x600000277480>{number = 4, name = (null)}
    并发异步:2 --> <NSThread: 0x604000465f80>{number = 3, name = (null)}
    并发异步:2 --> <NSThread: 0x604000466140>{number = 5, name = (null)}

    本例子中开了三条新线程。

    主线程同步

    - (void)mainQueueSync
    {
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"任务1:%@",[NSThread currentThread]);
        });
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"任务2:%@",[NSThread currentThread]);
        });
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"任务3:%@",[NSThread currentThread]);
        });
    }
    

    输出结果:

    主线程锁死,崩溃

    原因:主队列同步会先执行任务1,当前主线程正在执行mainQueueSync方法,造成执行任务1等待mainQueueSync方法结束,mainQueueSync方法等待任务1~3任务结束。

    线程通讯

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"做一些费时的东西");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"主线程刷新UI");
        });
    });
    

    输出结果:

    做一些费时的东西
    主线程刷新UI

    栅栏

    dispatch_queue_t queue = dispatch_queue_create("com.multithreading.fence", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务1:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务2:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"------并发异步执行栅栏分割------");
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务3:%i --> %@",i, [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务4:%i --> %@",i, [NSThread currentThread]);
        }
    });
    

    输出结果:

    任务2:0 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
    任务1:0 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
    任务2:1 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
    任务1:1 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
    任务2:2 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
    任务1:2 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
    ------并发异步执行栅栏分割------
    任务4:0 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
    任务3:0 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
    任务4:1 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
    任务3:1 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
    任务4:2 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
    任务3:2 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}

    异步是没有顺序的,但是添加了栅栏,可以将任务分为两部分,先做第一部分完毕后,才会做第二部分。

    队列组

    dispatch_group_t group = dispatch_group_create();
        dispatch_queue_t queue = dispatch_queue_create("com.multithreading.group", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务1:%i --> %@",i,[NSThread currentThread]);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务2:%i --> %@",i,[NSThread currentThread]);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务3:%i --> %@",i,[NSThread currentThread]);
        }
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务完了,回到主线程刷新UI");
    });
    

    输出结果:

    任务1:0 --> <NSThread: 0x60400027a080>{number = 3, name = (null)}
    任务3:0 --> <NSThread: 0x604000279fc0>{number = 5, name = (null)}
    任务2:0 --> <NSThread: 0x60400027a0c0>{number = 4, name = (null)}
    任务1:1 --> <NSThread: 0x60400027a080>{number = 3, name = (null)}
    任务2:1 --> <NSThread: 0x60400027a0c0>{number = 4, name = (null)}
    任务3:1 --> <NSThread: 0x604000279fc0>{number = 5, name = (null)}
    任务1:2 --> <NSThread: 0x60400027a080>{number = 3, name = (null)}
    任务2:2 --> <NSThread: 0x60400027a0c0>{number = 4, name = (null)}
    任务3:2 --> <NSThread: 0x604000279fc0>{number = 5, name = (null)}
    任务完了,回到主线程刷新UI

    开辟了三条线程,在任务1,2,3完成后会,回调dispatch_group_notify方法,回到主线程刷新UI。

    3、NSOperation

    NSOperation的创建:

    • 使用NSInvocationOperation子类,需要start开启
    • 使用NSBlockOperation子类,需要start开启
    • 使用继承NSOperation的子类,需要start开启

    NSInvocationOperation

    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation:) object:@"invocation"];
    [invocationOperation start];
    
    - (void)invocationOperation:(NSObject *)object
    {
        NSLog(@"invocation方法:%@",[NSThread currentThread]);
    }
    

    输出结果:

    invocation方法:<NSThread: 0x600000071ec0>{number = 1, name = main}

    没有开新线程。

    NSBlockOperation

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block方法:%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务1:%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务2:%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务3:%@",[NSThread currentThread]);
    }];
    [blockOperation start];
    

    输出结果:

    block方法:<NSThread: 0x7feaf1701590>{number = 1, name = main}
    任务1:<NSThread: 0x7f839bc21280>{number = 2, name = (null)}
    任务2:<NSThread: 0x7f839be44180>{number = 3, name = (null)}
    任务3:<NSThread: 0x7f839be1f470>{number = 4, name = (null)}

    blockOperationWithBlock方法:主线程实现,不加入队列就不会开辟新线程
    addExecutionBlock方法:会开新线程

    继承NSOperation的子类

    此例子中继承NSOperation的子类名为DZROperation ,在.m文件中重写main函数来实现任务

    // DZROperation.h
    #import <Foundation/Foundation.h>
    
    @interface DZROperation : NSOperation
    
    @end
    
    
    // DZROperation.m
    #import "DZROperation.h"
    
    @implementation DZROperation
    
    - (void)main
    {
        for (int i = 0; i < 3; i++) {
            NSLog(@"DZROperation --> %i, %@",i, [NSThread currentThread]);
        }
    }
    
    @end
    
    DZROperation *operation = [[DZROperation alloc] init];
    [operation start];
    

    输出结果:

    DZROperation --> 0, <NSThread: 0x7fd22ee01ae0>{number = 1, name = main}
    DZROperation --> 1, <NSThread: 0x7fd22ee01ae0>{number = 1, name = main}
    DZROperation --> 2, <NSThread: 0x7fd22ee01ae0>{number = 1, name = main}

    没有加入队列,是在主线程中实现,未开辟新线程。

    NSOperationQueue

    NSOperationQueue队列类型:

    • 主队列
    • 非主队列(串行,并发)

    队列NSOperationQueue有个参数最大并发数:maxConcurrentOperationCount

    • maxConcurrentOperationCount默认-1,直接开启并发,所以非主队列默认是开启并发
    • maxConcurrentOperationCount > 1,进行并发
    • maxConcurrentOperationCount = 1,表示不开线程,是串行
    • maxConcurrentOperationCount系统会限制一个最大值,所以设maxConcurrentOperationCount很大也是无意义的
    添加自定义NSOperation类到队列中
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    DZROperation *operation1 = [[DZROperation alloc] init];
    DZROperation *operation2 = [[DZROperation alloc] init];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    

    输出结果:

    DZROperation --> 0, <NSThread: 0x7fc46660c030>{number = 2, name = (null)}
    DZROperation --> 0, <NSThread: 0x7fc466605c70>{number = 3, name = (null)}
    DZROperation --> 1, <NSThread: 0x7fc46660c030>{number = 2, name = (null)}
    DZROperation --> 1, <NSThread: 0x7fc466605c70>{number = 3, name = (null)}
    DZROperation --> 2, <NSThread: 0x7fc46660c030>{number = 2, name = (null)}
    DZROperation --> 2, <NSThread: 0x7fc466605c70>{number = 3, name = (null)}

    此处的NSOperationQueue是默认并发队列,这里开了2个新线程

    直接添加任务
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    [queue addOperationWithBlock:^{
        NSLog(@"添加任务1:%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"添加任务2:%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"添加任务3:%@",[NSThread currentThread]);
    }];
    

    输出结果:

    maxConcurrentOperationCount = -1输出:
    添加任务2:<NSThread: 0x7fa3a2f169f0>{number = 3, name = (null)}
    添加任务1:<NSThread: 0x7fa3a2d09240>{number = 2, name = (null)}
    添加任务3:<NSThread: 0x7fa3a2d0a3d0>{number = 4, name = (null)}

    maxConcurrentOperationCount = 1输出:
    添加任务1:<NSThread: 0x7fe65270b6a0>{number = 2, name = (null)}
    添加任务2:<NSThread: 0x7fe65270b6a0>{number = 2, name = (null)}
    添加任务3:<NSThread: 0x7fe65270b6a0>{number = 2, name = (null)}

    直接在block中实现任务。

    operationQueue间通讯
    // 主队列
    NSOperationQueue *mainOperation = [NSOperationQueue mainQueue];
    // 并发队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        NSLog(@"做复杂操作");
        
        [mainOperation addOperationWithBlock:^{
            NSLog(@"刷新UI");
        }];
    }];
    

    输出结果:

    做复杂操作
    刷新UI

    任务的执行先后设定 -- 类似GCD的栅栏
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务1:%d, %@",i, [NSThread currentThread]);
        }
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任务2:%d, %@",i, [NSThread currentThread]);
        }
    }];
    
    // 任务1依赖任务2
    [operation1 addDependency:operation2];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    

    输出结果:

    任务2:0, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
    任务2:1, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
    任务2:2, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
    任务1:0, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
    任务1:1, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
    任务1:2, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}

    依赖关系,任务1依赖任务2,任务2完成后才能完成任务1。但是不能相互依赖,会造成锁死。

    最最后:
    详情demo请转接这里

    相关文章

      网友评论

          本文标题:iOS多线程

          本文链接:https://www.haomeiwen.com/subject/kwfkjftx.html