多线程(一)

基本概念

  1. 进程:系统中正在运行的一个应用程序。
  2. 线程:进程想要执行任务,必须要有线程(每1个进程至少要有个1条线程),线程是进程的基本单元,一个进程的所有任务都是在线程中执行。
  3. 线程的串行:1个线程中任务的执行时串行的,若1个线程中执行多个任务,只能一个个的按顺序执行。同一时间内,1个线程只能执行1个任务。
  4. 多线程
    • 一个进程中开启多条线程,每条线程可以并行(同时)执行不同的任务
    • 什么叫线程的并行
      • 并行即同时执行。若同时开启3条线程分别下载3个文件(文件A,文件B,文件C)
    • 多线程并发执行的原理
      • 同一时间CPU只能处理1条线程。多线程并发执行,只是CPU在多条线程之间进行切换,如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

5.多线程的优缺点

  • 优点
    • 能适当提高程序的执行效率
    • 能适当提高资源利用率(CPU,内存利用率)
  • 缺点
    • 开启线程需要占用一定的内存空间,若开启大量的线程,则会占用大量的内存空间,降低程序的性能
    • 线程越多,CPU在调度线程上的开销就越大
    • 程序设计更加复杂(线程之间的通信,多线程的数据共享)

6.iOS中多线程的实现方案

- pthread
- NSThread
- GCD
- NSOperation

NSThread

  • 创建方式

    • [alloc init];
      • 需要手动开启线程;
      • 可以拿到线程对象
  • 分离一条子线程;
    - 不可拿到线程对象;

  • 后台创建一条线程;

    • 不可拿到线程对象;
  • 线程状态

  • 新建

  • 就绪

  • 运行

  • 阻塞

  • 死亡(线程死亡之后,不能再重新启用)

  • 线程安全

    • 多个线程访问同一块区域
    • 增加互斥锁
    • 相关代码:@synchoronized(self){}(一般都是使用self)
    • 这叫线程同步
  • 原子属性和非原子属性

  • 用@porperty生命一个属性的时候,内部会进行三步操作
    1.生成一个有下划线的成员变量
    2.生成一个setter方法
    3.生成一个getter方法

  • 原子属性 : atomic(为setter方法加锁,默认就是atomic)

  • 非原子属性:nonatomic(不会为setter方法加锁)

  • 为什么开发中经常使用nonatomic而不是atomic

    • atomic 代表线程安全,需要消耗大量资源
    • nonatomic代表非线程安全,适合内存小的移动设备
    • 在开发中尽量使用nonatomic
    • 尽量避免多线程访问同一资源
    • 尽量将加锁、资源抢夺的业务逻辑交给服务器,从而减少客户端的压力
  • 线程间通信

  • 把程序中的耗时操作尽量放到子线程当中进行

  • 必须要回到主线程中进行UI刷新

GCD

  • 两个核心概念

    • 任务 : 执行什么操作
    • 队列 : 用来储存任务的
  • 同步函数/异步函数

  • 同步函数:只能在当前线程中进行任务,不具备开启子线程的能力,立刻马上执行任务

  • 异步函数:可以在新线程中执行任务,具备开启子线程的能力

  • 并发队列/串行队列

  • 并发队列 : 多个任务可以同时执行,前提是在异步函数的情况下

  • 可以创建并发队列,也可以获取并发队列。GCD里本身存在一个并发队列

  • 串行队列 : 任务不能同时执行,需一个接一个的执行

  • 主队列 : 放在主队列里面的任务只能在主线程中执行,并且也是一个接一个的执行任务

  • GCD的使用

  • 异步函数 + 并发队列 : 开启子线程,并发执行任务

     //1.创建队列
    dispatch_queue_t  queue =          dispatch_queue_creat("one",DISPATHC_QUEUE_CONCURRENT);
     //2.封装任务,将任务添加到队列中
    dispathc_async(queue,^{
    
    NSLog(@"xxxx");
    
    });
    
  • 异步函数 + 串行队列 : 开启一条线程,串行执行任务

 //1.创建队列
 dispatch_queue_t queue = dispatch_queue_creat("two",DISPATCH_QUEUE_SERIAL);

 dispatch_async(queue,^{

  NSLog(@"xxxx");

  });
  • 同步函数 + 并发队列 : 不开线程,串行执行任务
  //1.创建队列
  dispatch_queue_t queue = dispatch_queue_creat("three",DISPATCH_QUEUE_CONCURRENT);

  dispatch_sync(queue,^{

  NSLog(@"xxx");

  });
  • 同步函数 + 串行队列 : 不开线程,串行执行任务
//1.创建队列
   dispatch_queue_t queue = dispatch_queue_creat("four",DISPATCH_QUEUE_SERIAL);
  dispatch_sync(queue,^{

    NSLog(@"xxx");
  });
  • 异步函数 + 主队列 : 不开线程,在主线程中串行执行任务
   //1.获得主队列
  dispatch_queue_t queue = dispatch_get_main_queue();
  //
  dispatch_async(queue,^{

     NSLog(@"xxx");

  });

  • 同步函数 + 主队列 : 造成死锁状况

    //获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    

//
dispatch_sync("six",^{

NSLog(@"xxx");
});


 
- GCD中的线程通信

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    NSURL * url = [NSURL URLWithString:@"http://static.jstv.com/img/2016/5/18/20165181463528366178_0.jpg"];
    
    NSData * imagedate = [NSData dataWithContentsOfURL:url];
    
    UIImage * image = [UIImage imageWithData:imagedate];
    
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        self.imageView.image = image;
    });
  
    
});

}

 
- GCD的常用函数
 - 延迟

![GCD中的延迟](http://upload-images.jianshu.io/upload_images/1404354-5a589a7e984c4150.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![调用延迟方法](http://upload-images.jianshu.io/upload_images/1404354-b691033dde114ed0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![NSTimer](http://upload-images.jianshu.io/upload_images/1404354-63a320ee97f5eea8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 - 一次代码       

![一次代码](http://upload-images.jianshu.io/upload_images/1404354-18b974230d99a0ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 栅栏函数
    
    - 用于异步函数
    - 用于控制多线程执行任务顺序
    - 在使用栅栏函数的时候,*不能使用全局并发队列,只能进行手动创建*。
    - 栅栏函数之前的线程执行顺序,栅栏函数是没有办法进行控制的
![栅栏函数](http://upload-images.jianshu.io/upload_images/1404354-7b9aa4054951553b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 快速迭代

 - 开启多个线程,完成快速迭代操作
 - 类似于for循环
 - GCD里面的快速迭代是并发队列
 - for循环里面是串行队列
        小案例:图片的移动
        思路:(使用了GCD里面的快速迭代)
         1.获得最初文件夹的路径
         2.获得目的文件夹的为路径
         3.移动文件需要全路径,需要对最初文件夹下的文件进行路径拼接
         4.文件名不变,所以目的文件夹的文件路径也需要进行拼接
         5.然后用文件管理者进行文件移动

- 案例代码





dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {

  1. 第一个参数:遍历的次数
  2. 第二个参数: 队列,必须使用并发队列
    3.第三个参数:设置索引

};


![GCD快速迭代 图片移动](http://upload-images.jianshu.io/upload_images/1404354-b1633fd217ea64d3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
- 队列组

  - 用来控制队列任务的完成情况
  
// 创建队列组
dispatch_group_t group = dispatch_group_create();

// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

//将队列添加到队列组中,执行任务(下载图1)
dispatch_group_async(group, queue, ^{
    
    //确定图片地址
    NSURL * url = [NSURL URLWithString:@"http://img.2258.com/d/file/yule/mingxing/neidi/2016-04-20/6b6d95c044b5282cf5b8c78f73c23c4c.jpg"];
    
    //根据图片地址下载二进制数据
    NSData * imageData = [NSData dataWithContentsOfURL:url];
    
    //转换二进制数据
    self.image1 = [UIImage imageWithData:imageData];

});

//将队列添加到队列组中,执行任务(下载图2)
dispatch_group_async(group, queue, ^{
    
    //确定图片地址
    NSURL * url = [NSURL URLWithString:@"http://pic.yesky.com/uploadImages/2016/126/00/7HLRG65LQ5FJ.jpg"];
    
    //根据图片地址下载二进制数据
    NSData * imageData = [NSData dataWithContentsOfURL:url];
    
    //转换二进制数据
    self.image2 = [UIImage imageWithData:imageData];
    
});


//当队列组中的任务完成之后会进入这个方法

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    //开启图片上下文
    UIGraphicsBeginImageContext(CGSizeMake(200, 200));
    
    //绘制图片到上下文中
    [self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
    [self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
    
    //获得新图片
    UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //关闭图片上下文
    UIGraphicsEndImageContext();
    
    //回到主线程,刷新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        
         // 设置图片
        self.image.image = newImage;
        
    });
    
});

- 这个方法内部并不是阻塞,内部本身是异步的
![方法内部](http://upload-images.jianshu.io/upload_images/1404354-100b4d4670a1fc69.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 这个方法是阻塞的,会等之前的任务执行完成之后才能执行
![等待](http://upload-images.jianshu.io/upload_images/1404354-c561530c74eb514e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 关于队列组的另一种写法

//获得全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

//创建队列组
dispatch_group_t group = dispatch_group_create();

//在该方法后面的异步任务会被纳入监听范围,进入队列组
dispatch_group_enter(group);

dispatch_async(queue, ^{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    //任务执行完成之后离开队列组
    dispatch_group_leave(group);
});


dispatch_group_enter(group);

dispatch_async(queue, ^{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    dispatch_group_leave(group);
    
});

- 两个异步函数的方法的区别

      1. 一个用block块封装任务
      2.一个用函数来进行任务的封装

![两个方法的区别](http://upload-images.jianshu.io/upload_images/1404354-dcf9193766c33fbf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 全局并发队列和手动创建的并发队列的区别

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

推荐阅读更多精彩内容