iOS中的多线程编程

来源:互联网 发布:使用软件 编辑:程序博客网 时间:2024/05/01 05:50

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_notifydispatch_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_waitdispatch_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.mNSOperationQueue * 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.mMyOperation * op1 = [[MyOperation alloc] init];[op1 start];[NSThread sleepForTimeInterval:3.0f];[op1 cancel];
0 0
原创粉丝点击