iOS 再学多线程(一)

噢,达令

之前“写”过一篇关于多线程的博客,主要是综合各路大神的博文写得,回过头看其实对于多线程的相关知识点还是模糊的,从今天起重新认识多线程。

基础知识

进程、线程

进程
进程

进程,指的是系统中正在运行的一个应用程序,每个进程是独立的,简言之: 正在进行的程序即进程。
线程和线程是密不可分的,一个进程想要运行并执行任务,必须有线程才行。一个进程的所有任务都在线程中执行的。
例如: 使用迅雷下片儿都需要线程,一般我们在工程中,将耗时的任务放到子线程中执行。

线程
线程

串行

在同一时间内,一个线程只能执行一个任务,在串行中,如果要在一个线程中执行多个任务(串行),那么只能一个一个的按顺序执行这些任务。

例如排队办业务,别插队,挨打!
例如排队办业务,别插队,挨打!

多线程

一个进程中可以开启多条线程,每条线程可以并行(同时)进行,用于执行不同的任务。

下片儿
下片儿

如图:

并行
并行

多线程原理

同一时间,CPU 只能处理一条线程的任务,只有一条线程工作。多线程并发同时执行,其实是 CPU 快速地在多条线程之间调度,来回切换调度,造成了“同时”执行的假象,只不过 CPU 切换时间很快。其实也可以理解为“串行”。

多线程的缺点

  1. 多线程如果在很多条线程之间切换调度, CPU 会消耗大量的 CPU 资源,CPU 的开销会很大,很累;
  2. 多线程开的过多会降低每条线程的调动频次,造成线程执行效率低;
  3. 创建线程对内存是有开销的,而且需要时间,大约 90 毫秒的创建时间,所以创建的线程越多占用空间还有耗费时间也会越多、越久;
  4. 线程创建的越多,对于项目的设计会更加复杂,比如线程之间的通讯,多线程的数据共享。

常用多线程方案对比

以下为我们项目中的常用多线程方案,其中 GCD 多线程方案,使用相对多,相对“牛逼“,恩,是牛逼,面试没回答好 /(ㄒoㄒ)/ 。其中的 pthread 使用时需要导入 pthread 头文件,使用 C 语言实现。

多线程方案对比
多线程方案对比

NSThread 线程创建

我们在 viewController 中创建子线程,创建方法常用的有 ”3种“。

#pragma mark - 在touche方法中调用创建线程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self creatThread1];
    [self creatThread2];
    [self creatThread3];

}


#pragma mark -- 隐式创建子线程 (不能更详细的设置线程相关的信息)
- (void)creatThread3{
    
    [self performSelector:@selector(run:) withObject:@"jake" ];
}

#pragma mark -- 自动创建线程 自动开启
- (void)creatThread2{

    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"Mob"];
}

#pragma mark - 手动创建 线程创建完,会自动销毁,不用程序员管理

- (void)creatThread1{

    //创建线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"run"];
    //线程命名
    thread.name = @"线程1";
    //开启线程
    [thread start];
}


- (void)run: (NSString *)pat{

    NSLog(@"pat : %@ --- %@ ", pat , [NSThread currentThread].name);
    
    [NSThread sleepForTimeInterval:2];//阻塞线程,让线程休眠两秒后执行
    
    //遥远的未来 暂停到某个时刻
    //[NSThread sleepUntilDate:[NSDate distantFuture]];
        NSLog(@"阻塞线程,让线程休眠两秒后执行");
}

多线程安全

多条线程访问同一个地址,或是同时做同一件事情时,会出现访问错误的情况,例如买票、取款。

解决方法: 需要使用线程锁(互斥锁),在取值前添加,一般我们将 self 作为锁对象,锁对象一定要注意,只有一个,多个锁对象还会出现访问问题。

线程同步和线程锁为同一件事。

@synchronized (self) {
    
}

GCD 多线程

全称是Grand Central Dispath (牛逼的中枢调度器),纯C语言,提供非常多强大的函数,是目前苹果官网推荐的多线程开发方法,NSOperation便是基于GCD的封装。

GCD中有2个核心概念

(1)任务:执行什么操作
(2)队列:用来存放任务

容易混淆:同步、异步、并发、串行

同步和异步主要影响:能不能开启新的线程

同步:只是在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力

并发和串行主要影响:任务的执行方式

并发:允许多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务

并行和串行不会影响是否能开启线程。

GCD 自己可以创建 串行队列, 也可以创建并行队列.它有两个参数,第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。第二个才是最重要的。第二个参数用来表示创建的队列是串行的还是并行的,传入DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。

//串行队列
dispatch_queue_t queue1 = dispatch_queue_create("GCD多线程,串行", DISPATCH_QUEUE_SERIAL);

// 异步任务
dispatch_async(queue1, ^{
   NSLog(@"异步执行任务,开线程,在串行队列中执行");
   });
}];


 //并行队列
dispatch_queue_t queue2 = dispatch_queue_create("GCD多线程,并行", DISPATCH_QUEUE_CONCURRENT);
也可以:
dispatch_queue_t queue2 = dispatch_queue_create("GCD多线程,并行", NULL);

//同步任务
dispatch_sync(queue2, ^{
NSLog(@"同步任务:不会开启线程,在并行队列中执行");

});

GCD 官方已经提供了一个全局的并发队列,供整个应用使用

dispatch_queue_t queque3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_barrier_async 任务 “栅栏”

前面任务执行完之后才会执行它,执行完之后才会执行后面的。

    // 全局并发队列  在 barrier 任务中不能使用全局队列 ×
dispatch_queue_t quent = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
// 要使用这个队列 √
dispatch_queue_t quent2 = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);

dispatch_async(quent2, ^{
    
    NSLog(@"---1--- %@",[NSThread currentThread]);
});

// “栅栏” 前面的任务执行完后,再执行 barrier 里面的,执行完之后才会执行后面的任务
dispatch_barrier_async(quent2, ^{
    
    NSLog(@"---栅栏 2--- %@",[NSThread currentThread]);
    
    [self run:@"li"];
    
});

dispatch_async(quent2, ^{
    NSLog(@"---3--- %@",[NSThread currentThread]);
});

主队列 特殊的队列

在主队列中的任务都会在主线程中进行。

dispatch_queue_t queue4 = dispatch_get_main_queue();
同步、异步 并发和串行队列组合区别
同步、异步 并发和串行队列组合区别

由于队列和任务的不同,可以搭配出不同的组合,如上图,大体上的问题有两个,一个是否会创建新的子线程,还有是否会阻塞线程。

异步函数+并发队列

可以同时开启多条线程

同步函数 + 并发队列

不可以开新的线程,不能并发执行任务(只要不能开辟新的线程就不会并发执行任务)

异步函数 + 串行队列

可以开辟新的子线程,但是不能并发执行任务,只能开一条线程执行任务

异步函数 + 主队列

不管你是同步还是异步操作,只要是主队列,那么任务执行都会在主线程中执行,异步函数在主队列中将不会开辟线程。

同步函数 + 串行队列 同步函数 + 主队列

这两个组合比较特殊,一般不会在项目中这样干活搭配,这样的组合会造成堵塞线程。”你先“, ”还是你先“ 让来让去,谁都不能走了。

描述
描述

因为主队列要在主线程中执行,我们假设函数就在当前的主线程中,当执行主队列时,他想回到主线程,但是执行主线程的时候,需要同步的任务执行完才能执行主线程。然而此刻同步任务也想执行完,但是主线程还没执行完。把自己绕蒙逼了 /(ㄒoㄒ)/~~

简而言之: 主队列要执行,就得回到主线程,但是,同步任务还没执行完毕,无法完成祖国回归,回到主线程怀抱的梦想,所以就很尴尬了,最终隔岸相望。

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

推荐阅读更多精彩内容