iOS知识点总结(5)- 多线程

进程:一个程序的一次运行,在执行过程中拥有独立的内存单元,而多个线程共享一块内存

线程:线程是指进程内的一个执行单元

区别:

(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行

(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.

(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

NSThread:

直接操控线程对象,非常直观和方便 ,但是,线程的生命周期还是需要我们手动管理。

创建并启动:

NSThread  *thread = [[NSThread  alloc] initWithTarget:self  selector:@selector(run:) object:nil];;

[thread  start];

创建并自动启动

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];

其他操作方法:

//取消线程

- (void)cancel;

//启动线程

- (void)start;

//判断某个线程的状态的属性

@property (readonly, getter=isExecuting) BOOL executing;

@property (readonly, getter=isFinished) BOOL finished;

@property (readonly, getter=isCancelled) BOOL cancelled;

//设置和获取线程名字

-(void)setName:(NSString *)n;

-(NSString *)name;

//获取当前线程信息

+ (NSThread *)currentThread;

//获取主线程信息

+ (NSThread *)mainThread;

//使当前线程暂停一段时间,或者暂停到某个时刻

+ (void)sleepForTimeInterval:(NSTimeInterval)time;

+ (void)sleepUntilDate:(NSDate *)date;

NSOperation和NSOperationQueue:

NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列

NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation

创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。

//1.创建NSInvocationOperation对象

  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

  //2.开始执行

  [operation start];

//1.创建NSBlockOperation对象

  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

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

  }];

  //2.开始任务

  [operation start];

但是 NSBlockOperation 还有一个方法:addExecutionBlock: ,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务

队列

主队列:NSOperationQueue *queue = [NSOperationQueue mainQueue];

其他队列: NSOperationQueue  *queue = [[NSOperationQueue alloc] init];

//2.创建NSBlockOperation对象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

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

}];

//3.添加多个Block

for (NSInteger i = 0; i < 5; i++) {

    [operation addExecutionBlock:^{

        NSLog(@"第%ld次:%@", i, [NSThread currentThread]);

    }];

}

//4.队列添加任务

[queue addOperation:operation];

添加依赖:

[operation2 addDependency:operation1];//任务二依赖任务一

[operation3 addDependency:operation2]; 

NSOperation:

BOOL executing; //判断任务是否正在执行

BOOL finished; //判断任务是否完成

void (^completionBlock)(void); //用来设置完成后需要执行的操作

- (void)cancel; //取消任务

- (void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕

NSOperationQueue:

NSUInteger operationCount; //获取队列的任务数

- (void)cancelAllOperations; //取消队列中所有的任务

- (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕

[queue setSuspended:YES]; // 暂停queue

[queue setSuspended:NO]; // 继续queue

从其他线程回到主线程的方法:

NSThread:[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];

GCD://Objective-C

dispatch_async(dispatch_get_main_queue(), ^{

});

NSOperationQueue://Objective-C

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];

GCD:

1. GCD 简介

是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统

GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程);

程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码。

2. GCD 任务和队列

GCD 中两个核心概念:『任务』 和 『队列』行任务有两种方式:『同步执行』 和 『异步执行』在 GCD 中有两种队列:『串行队列』 和 『并发队列』

3. GCD 的使用步骤

可以使用 dispatch_queue_create 方法来创建队列 

第一个参数表示队列的唯一标识符,用于 DEBUG,可为空。队列的名称推荐使用应用程序 ID 这种逆序全程域名。

第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列

// 串行队列的创建方法

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

// 并发队列的创建方法

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

// 主队列的获取方法

dispatch_queue_t queue = dispatch_get_main_queue();

// 全局并发队列的获取方法

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

GCD 提供了同步执行任务的创建方法 dispatch_sync 和异步执行任务创建方法 dispatch_async。

4. GCD 的基本使用(六种组合不同区别,队列嵌套情况区别,相互关系形象理解)

参考 https://www.jianshu.com/p/2d57c72016c6

5. GCD 线程间的通信

/**

* 线程间通信

*/

- (void)communication {

    // 获取全局并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 获取主队列

    dispatch_queue_t mainQueue = dispatch_get_main_queue();


    dispatch_async(queue, ^{

        // 异步追加任务 1

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程


        // 回到主线程

        dispatch_async(mainQueue, ^{

            // 追加在主线程中执行的任务

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

        });

    });

}

6. GCD 的其他方法

GCD 栅栏方法:dispatch_barrier_async

我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于 栅栏 一

样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到

dispatch_barrier_async 方法在两个操作组间形成栅栏。

/**

* 栅栏方法 dispatch_barrier_async

*/

- (void)barrier {

    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{

        // 追加任务 1

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

    });

    dispatch_async(queue, ^{

        // 追加任务 2

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

    });

    dispatch_barrier_async(queue, ^{

        // 追加任务 barrier

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程

    });

    dispatch_async(queue, ^{

        // 追加任务 3

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程

    });

    dispatch_async(queue, ^{

        // 追加任务 4

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程

    });

}

GCD 延时执行方法:dispatch_after:

我们经常会遇到这样的需求:在指定时间(例如 3 秒)之后执行某个任务。可以用 GCD 的dispatch_after 方法来实现。

需要注意的是:dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after 方法是很有效的。

GCD 一次性代码(只执行一次):dispatch_once

- (void)once {

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        // 只执行 1 次的代码(这里面默认是线程安全的)

    });

}

GCD 快速迭代方法:dispatch_apply:

- (void)apply {

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    NSLog(@"apply---begin");

    dispatch_apply(6, queue, ^(size_t index) {

        NSLog(@"%zd---%@",index, [NSThread currentThread]);

    });

    NSLog(@"apply---end");

}

GCD 队列组:dispatch_group:

调用队列组的dispatch_group_async先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的dispatch_group_enter、dispatch_group_leave组合来实现dispatch_group_async。

调用队列组的dispatch_group_notify回到指定线程执行任务。或者使用dispatch_group_wait回到当前线程继续向下执行(会阻塞当前线程)。

- (void)groupNotify {

    dispatch_group_t group =  dispatch_group_create();

    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // 追加任务 1

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

    });

    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // 追加任务 2

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{

        // 等前面的异步任务 1、任务 2 都执行完毕后,回到主线程执行下边任务

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程

        NSLog(@"group---end");

    });

}

dispatch_group_wait:

暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行

// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_group_enter、dispatch_group_leave

dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1

dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。

当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。

/**

* 队列组 dispatch_group_enter、dispatch_group_leave

*/

- (void)groupEnterAndLeave {

    dispatch_group_t group = dispatch_group_create();

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_enter(group);

    dispatch_async(queue, ^{

        // 追加任务 1

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

        dispatch_group_leave(group);

    });

    dispatch_group_enter(group);

    dispatch_async(queue, ^{

        // 追加任务 2

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程


        dispatch_group_leave(group);

    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{

        // 等前面的异步操作都执行完毕后,回到主线程.

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

    });

}

GCD 信号量:dispatch_semaphore:

Dispatch Semaphore 提供了三个方法:

dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量

dispatch_semaphore_signal:发送一个信号,让信号总量加 1

dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。

/**

* semaphore 线程同步

*/

- (void)semaphoreSync {

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    __block int number = 0;

    dispatch_async(queue, ^{

        // 追加任务 1

        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        number = 100;

        dispatch_semaphore_signal(semaphore);

    });

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    NSLog(@"semaphore---end,number = %zd",number);

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容