多线程

一、多线程概述

程序、进程、线程

程序:由源代码生成的可执行应用(例如:QQ.app)

进程:一个正在运行的程序可以看做一个进程。(例如:正在运行的 QQ就是一个进程),进程拥有独立运行所需的全部资源

线程:程序中独立运行的代码段。(例如:接收QQ消息的代码)

一个进程是由一或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。

单线程

  • 每个正在运行的程序(即进程),至少包含一个线程,这个线程叫主线程。
  • 主线程在程序启动时被创建,用于执行main函数。
  • 只有一个线程即主线程的程序,称作单线程程序。
  • 在单线程程序中,主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行。

多线程

  • 拥有多个线程的程序,称作多线程程序。
  • iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程。
  • 可以根据需要开辟若干子线程
  • 子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行。

单、多线程区别

  • 单线程程序:只有一个线程,即主线程,代码顺序执行,容易出现代码阻塞(页面假死)。
  • 多线程程序:有多个线程,线程间独立运行,能有效地避免代码阻塞,并且提高程序的运行性能。
  • 注意:iOS中关于UI的添加和刷新必须在主线程中操作。

二、iOS平台下的多线程

iOS多线程实现种类

  • NSThread
  • NSOperationQueue
  • NSObject
  • GCD

---------------------NSThread和NSObject------------------

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"测试");
    /*
    // 线程休眠5秒
    [NSThread sleepForTimeInterval:5.0];
    
    // [NSThread currentThread] 获取当前的线程
    NSLog(@"current ==== %@", [NSThread currentThread]);
    
    // [NSThread mainThread] 获取主线程
    NSLog(@"main ==== %@", [NSThread mainThread]);
    
    // [NSThread isMainThread] 判断当前线程是否是主线程
    NSLog(@"isMainThread = %d", [NSThread isMainThread]);
   */
    
    // NSThread手动开辟子线程
    // 第一个参数:target
    // 第二个参数:action
    // 第三个参数:传参
//    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sayHi) object:nil];
    
    // 让线程开始执行
//    [thread start];
    
    // [thread isExecuting] 判断一个线程是否正在执行
//    NSLog(@"isExecuting = %d", [thread isExecuting]);
    
    // [thread isFinished] 判断一个线程是否完成了任务(是否执行完毕)
//    NSLog(@"isFinished = %d", [thread isFinished]);
    
    // 结束线程 给线程发送结束消息
//    [thread cancel];
    
    // NSThread自动开辟线程
//    [NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];
    
    // NSObject开辟子线程
    // 第一个参数:selector,子线程执行的代码
    // 第二个参数:表示selector传递的参数
    // 这是NSObject中存在的后台执行的方法
    [self performSelectorInBackground:@selector(sayHi) withObject:nil];
    
    self.view.backgroundColor = [UIColor cyanColor];
    
}
- (void)sayHi
{
    NSLog(@"current ==== %@", [NSThread currentThread]);
    NSLog(@"main ==== %@", [NSThread mainThread]);
    
    for (int i = 0; i < 1000; i++)
    {
        NSLog(@"i ==== %d",i);
        if (i == 500)
        {
            // 立即结束线程
            // 写在哪里 哪个线程就关闭了 注意:不要随意地使用,使用时一定要注意当前的线程是否是主线程
//            [NSThread exit];
        }
    }
    // NSObject中存在的回到主线程执行的方法
    // 第一个参数:回到主线程做的事情
    // 第二个参数:传递的参数
    // 第三个参数:直到当前的线程已经结束才去执行这个方法
    [self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
}
- (void)onMainThread
{
    self.view.backgroundColor = [UIColor orangeColor];
    
    NSLog(@"current ==== %@", [NSThread currentThread]);
    
    NSLog(@"main ==== %@", [NSThread mainThread]);
    
    NSLog(@"isMainThread ==== %d", [NSThread isMainThread]);
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

---------------NSOperation和NSOperationQueue-------------

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /*
     NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类
     因为它是抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation或NSBlockOperation)来执行实际任务
     NSOperation(含子类),只是一个操作,本身无主线程、子线程之分,可在任意线程中使用,它们本身和多线程没有任何关系,不会进行线程的创建,只是封装了一定的代码段和数据去实现一个功能。通常与NSOperationQueue结合使用
    */
    
    // NSInvocationOperation类封装了执行操作的target和要执行的action
//    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(hehehe) object:nil];
    
    // 开始去执行
//    [operation start];
    
    // NSBlockOperation封装了需要执行的代码块
//    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
//        
//        NSLog(@"main1 ==== %@", [NSThread mainThread]);
//        NSLog(@"current1 ==== %@",[NSThread currentThread]);
//        
//    }];
    
    // 开始去执行
//    [blockOperation start];
    
    /*
     NSOperationQueue是操作队列,它用来管理一组NSOperation对象的执行,会根据需要自动为NSOperation开辟合适数量的线程,以完成任务的并行执行
     在开发过程中,我们不需要管理线程的创建和销毁
     NSOperation类可以调节它在队列中的优先级(使用addDependency:设置依赖关系)
     当最大并发数设置为1的时候能实现线程同步(串行执行)
    */
    
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
//    queue.maxConcurrentOperationCount = 2;
    
    // 给两个NSOperation对象添加一个依赖关系,它们就会根据依赖关系顺序执行
//    [operation addDependency:blockOperation];
    
//    [queue addOperation:blockOperation];
//    [queue addOperation:operation];
    
    // 获取系统提供的主队列
//    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    
}
- (void)hehehe
{
    NSLog(@"main ==== %@", [NSThread mainThread]);
    NSLog(@"current ==== %@",[NSThread currentThread]);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    queue.maxConcurrentOperationCount = 10;
    
    for (int i = 0; i < 10; i++)
    {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            
            NSLog(@"%dcurrent = %@, main = %@", i, [NSThread currentThread], [NSThread mainThread]);
        }];
        [queue addOperation:operation];
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

---------------GCD(Grand Central Dispatch)-----------------

GCD简介

  • Grand Central Dispatch(GCD)是Apple开发的一种多核编程技术。主要用于优化应用程序以支持多核处理器以及其他对称多处理系统

  • GCD提供函数实现多线程开发,性能更高功能也更加强大

  • 它首次发布在Mac OS X10.6,iOS4及以上也可用

GCD核心概念

  • 任务:具有一定功能的代码段。一般是一个block或者函数

  • 分发队列:GCD以队列的方式进行工作,FIFO

  • GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务

GCD中两种队列

dispatch queue分为下面2种:

  • SerialQueue:一次只执行一个任务。Serial queue通常用于同步访问特定的资源和数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。SerialQueue能实现线程同步

  • Concurrent:可以并发地执行多个任务,但是遵守FIFO

GCD工程截图.png

--------------------ViewController.m----------------------

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
#pragma mark ---- GCD串行队列
    
    // 系统提供的一个串行队列
    // 里面的任务是在主线程依次去执行,也就是串行
    // 这个方法比较常用
//    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 创建一个串行队列
    // 第一个参数:队列的名字(苹果推荐使用反向域名去命名),也可以是0
    // 第二个参数:系统提供好的一个宏,表示串行队列系统保留字段
    // 这种方式创建的队列,它会自己去开辟一个子线程去完成队列里面的任务
//    dispatch_queue_t queue = dispatch_queue_create(0 , DISPATCH_QUEUE_SERIAL);
    
#pragma mark ---- 并行队列
    
    // 使用系统提供的并行队列
    // 这个方法比较常用
    // global queue 是苹果里面的全局队列,有4个优先级
    // DISPATCH_QUEUE_PRIORITY_BACKGROUND 表示队列的优先级
    // 第二个参数是苹果预留的参数,为了以后去使用,目前没有用到,填写0
//    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    
    // 自己创建一个并行队列
    // 第一个参数:表示这个队列的名字
    // 第二个参数:系统提供好的一个宏,表示并行队列
//    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    
    // 使用dispatch_async向队列中添加任务
//    dispatch_async(queue, ^{
//        
//    NSLog(@"main ==== %@,current ==== %@", [NSThread mainThread], [NSThread currentThread]);
//        
//    });
//    
//    dispatch_async(queue, ^{
//        
//        NSLog(@"main1 ==== %@,current1 ==== %@", [NSThread mainThread], [NSThread currentThread]);
//        
//    });
//    
//    dispatch_async(queue, ^{
//        
//        NSLog(@"main2 ==== %@,current2 ==== %@", [NSThread mainThread], [NSThread currentThread]);
//        
//    });
//    
//    dispatch_async(queue, ^{
//        
//        NSLog(@"main3 ==== %@,current3 ==== %@", [NSThread mainThread], [NSThread currentThread]);
//        
//    });
//    
//    // 延迟执行一段代码 几秒之后做某件事
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//        
//        NSLog(@"已经5秒之后了");
//        
//    });
    
#pragma mark ---- 重复的向一个队列中添加一系列的任务
    
//    dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    // 使用dispatch_apply()往队列中添加任务,任务会重复执行n次
    // 第一个参数:执行的次数
    // 第二个参数:在哪个队列里面去执行
    // 第三个参数:当前索引
//    dispatch_apply(100, queue, ^(size_t index) {
//        
//        NSLog(@"index ==== %zu", index);
//        
//    });

#pragma mark ---- 分组
    
    // 创建一个分组
    // dispatch_group_t主要用于把一些不相关的任务归为一组,组里面放的是队列
//    dispatch_group_t group = dispatch_group_create();
    
    // 创建一个并行队列
//    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    
    // 将任务添加到队列中,此任务执行的时候,其他任务停止执行
//    dispatch_barrier_async(queue, ^{
//        
//        NSLog(@"我执行时,其他任务停止执行");
//        
//    });
    
    // dispatch_group_async作用是往组里面的队列添加任务
    // 将任务添加到队列中,并添加分组标记
//    dispatch_group_async(group, queue, ^{
//        
//        NSLog(@"我是第1个任务");
//        
//    });
    
    // dispatch_group_notify作用:监听组里面的任务,等到组里面的任务全部执行完成之后,才会执行它里面的任务
    // 注意:在执行dispatch_group_notify之前最起码要向队列中放置一个任务才可以,否则notify里面的任务不会等待小组里面的其他任务执行完才执行
//    dispatch_group_notify(group, queue, ^{
//        
//        NSLog(@"无论如何,我是最后一个");
//        
//    });
//    
//    dispatch_group_async(group, queue, ^{
//        
//        NSLog(@"我是第2个任务");
//        
//    });
//    
//    dispatch_group_async(group, queue, ^{
//        
//        NSLog(@"我是第3个任务");
//        
//    });
//    
//    dispatch_group_async(group, queue, ^{
//        
//        NSLog(@"我是第4个任务");
//        
//    });
    
    // dispatch_once() 将任务添加到队列中,但任务在程序运行过程中,只执行一次
    // dispatch_sync() 将任务添加到队列中block不执行完,下面代码不会执行
    // dispatch_async_f() 将任务添加到队列中,任务是函数非block
    
    /*
     dispatch_once:该函数接收一个dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整形,实际上作为BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块
     dispatch_once不仅意味着代码仅会被运行一次而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题
    */
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    
    // sync会等待block体执行完成之后,才会去执行block体外面的代码
//    dispatch_sync(queue, ^{
//        
//        NSLog(@"第一个任务");
//        
//    });
//    NSLog(@"呵呵");
//    // async不等待block体执行完就去执行下面的代码
//    dispatch_async(queue, ^{
//        
//        NSLog(@"第二个任务");
//        
//    });
//    NSLog(@"逗比");
    
    // 第一个参数:队列
    // 第二个参数:函数参数的内容
    // 第三个参数:函数
    dispatch_async_f(queue, @"passValue", function);
}
void function(void * str)
{
    NSLog(@"这是一个函数,%@",str);
}
- (void)loadData
{
    NSURLSession *session = [NSURLSession sharedSession];
    
    NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil)
        {
            dispatch_async(dispatch_get_main_queue(), ^
            {
                //这里去做更新UI的事情
            });
        }
    }];
    [task resume];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

-----------------------MyObject.h------------------------

#import <Foundation/Foundation.h>

@interface MyObject : NSObject

+(MyObject *)sharedMyObject;

@end

-----------------------MyObject.m-----------------------

#import "MyObject.h"

static MyObject *object = nil;

@implementation MyObject

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

推荐阅读更多精彩内容