iOS中的多线程编程

iOS中的多线程编程


关于多线程编程

什么是多线程

简单的来说,并发应用程序中一个单独的执行路径,就是一个线程。

相关术语

  • 进程:用于指代一个正在运行的可执行程序,它可以包含多个线程。
  • 任务:用于指代抽象的概念,表示需要执行工作。
  • 线程的同步:同步就是协同步调,按预定的先后次序进行运行。线程的同步即一个线程执行完指定操作后,另个一个线程再执行。
  • 线程的互斥:线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。

iOS中的多线程编程

  • pthread(POSIX threads):一套C语言接口,定义了创建和管理线程的API,非常灵活,可以直接操作线程。
  • NSThread:一套OC接口,需要自己手动管理线程,比GCD和NSOperation更加轻量级。
  • GCD:是iOS4出的一套并发编程的接口,纯C语言接口。以队列为基础。
  • NSOperation:一套面向对象的OC API,不需要手动管理线程。能实现一些GCD实现不了的功能。

NSThread

基本操作

线程的创建
  • 类方法创建线程
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"helloworld"];
  • 实例方法创建线程
NSThread * th = [[NSThread alloc] initWithTarget:self selector:@selector(threadFun:) object:@"helloworld"];
    [th start];
  • 继承NSThread,重写main方法(与NSOperation类似)
//MyThread.h
@interface MyThread : NSThread
@end

//MyThread.m
@implementation MyThread
- (void)main {
    NSLog(@"this is MyThread");
}

//main.m
#import <Foundation/Foundation.h>
#import "MyThread.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyThread * th = [[MyThread alloc] init];
        [th start];
        [[NSRunLoop mainRunLoop] run];
    }
    return 0;
}
@end
  • 其他:NSObject中的方法
 - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
线程的基本管理
  • 获取主线程+ (NSThread *)mainThread
  • 获取当前线程+ (NSThread *)currentThread;
  • 阻塞当前线程+ (void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
  • 销毁消除当前线程(currentThread)+ (void)exit;
  • 取消线程- (void)cancel;:将线程的状体置为cancelled,并不会停止线程的执行
  • 线程键的通讯
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

线程同步与互斥

  • @synchronized(obj)对代码段进行加锁,同时只能一个线程访问该代码段
@synchronized(self) {
    i++;
    NSLog(@"%ld", i);
}
  • NSLock:与@synchronized类似
NSLock * lock = [[NSLock alloc] init];
if ([lock tryLock]) {
    [lock lock];
    i++;
    NSLog(@"%ld", i);
    [lock unlock];
}
  • NSConditionLock条件锁:在所的基础上添加了条件,只有满足对应的条件,才能打开相应的锁
- (void)startTest {
    _conLock = [[NSConditionLock alloc] init];
    [NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"top thread"];
    for (int i = 0; i < 200; i++) {
        [NSThread detachNewThreadSelector:@selector(threadFun2:) toTarget:self withObject:@(i)];
    }
}

- (void)threadFun:(id)obj {

    [_conLock lock];
    NSLog(@"%@", obj);
    [NSThread sleepForTimeInterval:1.0f];
    //加锁,条件为10
    [_conLock unlockWithCondition:10];
}

- (void)threadFun2:(id)obj {
    NSInteger value = [obj integerValue];

    //只有到value等于10时,才能成功加锁,否则阻塞
    [_conLock lockWhenCondition:value];
    NSLog(@"thread%@", obj);
    [NSThread sleepForTimeInterval:1.0f];
    //解锁,并把条件设为value + 3,则下次上锁的条件为13,一次类推13,16,19...
    [_conLock unlockWithCondition:value + 3];
}

输出结果:
这里写图片描述

  • NSRecursiveLock递归锁:同一个线程中,递归或多次调用加锁代码段,不会造成死锁
j = 0;
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"top thread1"];
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"top thread2"];

- (void)threadFun:(id)obj {
    [self testWith:obj];
}

- (void)testWith:(id)obj {
    [_recLock lock];
    NSLog(@"test recursive,thread:%@, lock:%ld", obj, j++);
    [NSThread sleepForTimeInterval:1.0f];
    if (j < 10) {
        [self testWith:obj];
    }
    [_recLock unlock];
}

输出结果:
这里写图片描述

  • NSDistributedLock分布式锁:用于多个进程之间互斥访问资源,一般用于Mac开发
条件

使用NSCondition,可以实现多线程的同步,解决类似生产者消费者问题。

#import "testThread.h"

@implementation testThread
{
    NSInteger i;
    NSCondition * _con;
    NSMutableArray * _products;
}

- (void)startTest {
    _con = [[NSCondition alloc] init];
    _products = [[NSMutableArray alloc] init];
    [NSThread detachNewThreadSelector:@selector(threadFun2:) toTarget:self withObject:@"thread2"];
    [NSThread detachNewThreadSelector:@selector(threadFun2:) toTarget:self withObject:@"thread3"];
    [NSThread detachNewThreadSelector:@selector(threadFun1:) toTarget:self withObject:@"thread1"];
}

- (void)threadFun1:(id)obj {
    while (1) {
        [NSThread sleepForTimeInterval:2.0f];
        [_products addObject:@"apple"];//生产者创造一个产品
        NSLog(@"%@ add one product, product count:%ld", obj, _products.count);
        [_con signal];//通知消费者,消费产品
//        [_con broadcast];
    }
}

- (void)threadFun2:(id)obj {
    while (1) {
        [_con lock];
        [_con wait];//等待生产者的通知
        [_products removeObjectAtIndex:0];//消费者消费一个产品
        NSLog(@"%@ remove one product, proudct count:%ld", obj, _products.count);
        [_con unlock];
    }
}

@end

GCD

基本用法

队列
  • 串行队列:同时只能执行一个任务
    • 所有任务都在同一个线程中执行,FIFO
    • 多个串行队列之间是并行执行的,会占用多个线程
    • 创建串行队列:dispatch_queue_t serialQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    • dispatch_get_main_queue();获取主线程上的一个全局可用的串行队列
  • 并行队列:同时可以执行多个任务
    • 多个任务在不同的线程上执行,最大并发数(最多起的线程数)由操作系统根据当前的系统状态决定,不可手动设置(NSOperation可以)
    • 创建并行队列:dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    • dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);获取一个全局可用的并行队列
任务
  • 同步任务:添加一个任务后,需要等待任务执行完后才能返回
    • 无论是串行队列还是并行队列,同步任务都是在主线程上执行的
    • 同步任务会阻塞当前线程
    • 向主队列中添加同步任务会造成死锁,见死锁①
    • 向串行队列中添加嵌套的同步任务会造成死锁,见死锁②
//同步任务
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [NSThread sleepForTimeInterval:1.0f];
            //先打印
            NSLog(@"thread:%@", [NSThread currentThread]);
        });
NSLog(@"haha");//后打印

//死锁①
dispatch_sync(dispatch_get_main_queue(), ^{
            [NSThread sleepForTimeInterval:1.0f];
            NSLog(@"thread:%@", [NSThread currentThread]);
        });        
NSLog(@"haha");//造成死锁,haha永远不会打印

//死锁②
dispatch_queue_t serialQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
            NSLog(@"thread1:%@", [NSThread currentThread]);
            dispatch_sync(serialQueue, ^{
                NSLog(@"thread2:%@", [NSThread currentThread]);//造成死锁,thread2永远不会打印
            });
        });
  • 异步任务:添加一个任务后不需要等待任务执行完成
//异步任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [NSThread sleepForTimeInterval:1.0f];
            //后打印
            NSLog(@"thread:%@", [NSThread currentThread]);
        });
NSLog(@"haha");//先打印

其他用法

  • dispatch_after:延迟指定的时间后,将任务添加到指定的队列中
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0f * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"haha");
});
  • dispatch_group:将多个任务或队列归为一组,在这组的所有任务都执行完之后再执行后面的操作
    • dispatch_group_async函数:创建一个指定队列中的异步任务,并将其添加到指定组中
    • dispatch_group_notify函数:指定组中所有任务都执行完后,会执行该函数所指定的的任务
    • dispatch_group_wait函数:阻塞当前线程,等待group中所有任务执行完毕
//使用dispatch_group_notify
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"4");
    [NSThread sleepForTimeInterval:1.0f];
});

dispatch_group_notify(group, concurrentQueue, ^{
    NSLog(@"5");//"1234"全部打印后,才会打印5
});

//使用dispatch_group_wait
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
    NSLog(@"4");
    [NSThread sleepForTimeInterval:1.0f];
});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

NSLog(@"haha");//"dispatch_group_wait"会阻塞线程,所有haha会在"1234"之后打印
  • diapatch_once:执行一次
  • dispatch_apply:按照指定的次数将指定的Block追加到指定的DispatchQueue中,并等待全部处理执行结束
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t t) {
    NSLog(@"%zu", t);
    [NSThread sleepForTimeInterval:1.0f];
});
NSLog(@"haha");//等待dispatch_apply中的所有任务都执行完之后才会带引"haha"
  • dispatch_suspend / dispatch_resume
    • dispatch_suspend:挂起指定的队列
    • dispatch_resume:恢复指定的队列
  • Dispatch Semaphore:信号量,用于排他控制
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

for (int i = 0; i < 10; i++) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        //排他,同时只能有一个线程进入这个代码块,相当于锁
        NSLog(@"end wait, result:%ld", result);
        [NSThread sleepForTimeInterval:1.0f];

        dispatch_semaphore_signal(semaphore);
    });
}

NSOperation

基本用法

  • NSBlockOperation:以Block的形式定义任务
@interface testOperation ()
@property (nonatomic, strong) NSOperationQueue * queue;
@end

@implementation testOperation

- (void)startTest {

    //创建操作
    NSOperation * oper1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"test block operation1, current thread:%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0f];
    }];

    NSOperation * oper2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"test block operation2, current thread:%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0f];
    }];

    NSOperation * oper3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"test block operation3, current thread:%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0f];
    }];

    NSOperation * oper4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"test block operation4, current thread:%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0f];
    }];

    self.queue = [[NSOperationQueue alloc] init];
    //将操作加入队列中,操作就会在队列中并发执行,NSOperationQueue默认是并发队列
    [self.queue addOperation:oper1];
    [self.queue addOperation:oper2];
    [self.queue addOperation:oper3];
    [self.queue addOperation:oper4];
}
@end
  • NSInvocationOperation
@interface testOperation ()
@property (nonatomic, strong) NSOperationQueue * queue;
@end

@implementation testOperation

- (void)startTest {

    NSMethodSignature * sig = [[self class] instanceMethodSignatureForSelector:@selector(oper1Fun:andObj2:)];
    NSInvocation * invo = [NSInvocation invocationWithMethodSignature:sig];
    [invo setTarget:self];
    [invo setSelector:@selector(oper1Fun:andObj2:)];
    NSString * arg1 = @"haha";
    NSString * arg2 = @"oooo";
    [invo setArgument:&arg1 atIndex:2];
    [invo setArgument:&arg2 atIndex:3];
    NSOperation * oper1 = [[NSInvocationOperation alloc] initWithInvocation:invo];

    NSOperation * oper2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(oper2Fun) object:nil];

    self.queue = [[NSOperationQueue alloc] init];
    [self.queue addOperation:oper1];
    [self.queue addOperation:oper2];
}

- (void)oper1Fun:(id)obj1 andObj2:(id)obj2 {
    NSLog(@"test invocation operation1, current thread:%@, obj1:%@, obj2:%@", [NSThread currentThread], obj1, obj2);
    [NSThread sleepForTimeInterval:1.0f];
}

- (void)oper2Fun {
    NSLog(@"test invocation operation2, current thread:%@", [NSThread currentThread]);
}
@end
  • 继承NSOperation,重写main方法
@interface MyOperation : NSOperation
@end

@implementation MyOperation
- (void)main {
    NSLog(@"MyOperation, current thread:%@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0f];
}
@end

//main.m
NSOperationQueue * queue = [[NSOperationQueue alloc] init];

MyOperation * op1 = [[MyOperation alloc] init];
MyOperation * op2 = [[MyOperation alloc] init];
MyOperation * op3 = [[MyOperation alloc] init];
MyOperation * op4 = [[MyOperation alloc] init];
MyOperation * op5 = [[MyOperation alloc] init];
MyOperation * op6 = [[MyOperation alloc] init];

[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
[queue addOperation:op6];

其他用法

  • 设置最大并发数
    self.queue.maxConcurrentOperationCount = 3;
  • 设置操作之间的依赖关系
    [oper2 addDependency:oper1];//oper2依赖于oper1,即必须oper1执行完之后才能执行oper2
  • 手动管理NSOperation
    • 运行一个Operation:- (void)start;默认的start方法会直接进行一些异常判断,然后直接调用- (void)main;
    • 取消一个Operation:- (void)cancel;调用cancel方法并不会停止操作的执行,这取决于main方法中对cancel的处理。如果在main方法中没有对cancel进行处理,发送cancel消息是没有任何效果的
//MyOperation.m
@implementation MyOperation

- (void)start {
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
}

- (void)main {
    NSInteger index = 0;
    while (1) {
        NSLog(@"current thread:%@, index:%ld", [NSThread currentThread], index++);
        if (self.isCancelled) {
            NSLog(@"cancel");
            return;
        }
        [NSThread sleepForTimeInterval:1.0f];
    }
}
@end

//main.m
MyOperation * op1 = [[MyOperation alloc] init];
[op1 start];
[NSThread sleepForTimeInterval:3.0f];
[op1 cancel];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值