OC中多线程的使用、概念、创建方式、生命周期、使用注意等

来源:互联网 发布:东南大学没落 知乎 编辑:程序博客网 时间:2024/05/29 13:02

     进程

     什么是进程:在我们的系统中正在运行的程序

     进程的作用:负责给应用程序分配内存空间(该空间是受保护的,独立的)

     

     线程

     什么是线程:线程是CPU调度的最小单元,由CPU调度

     线程的作用:负责执行应用程序中的代码,在系统中运行着的程序的代码只能由线程执行

     线程创建过程:应用程序在启动过程中,系统会自动创建默认的线程,也就是程序的主线程/UI线程

     

     线程与进程之间的关系:一个进程至少有一个线程(即主线程),一个进程中可以有多个线程

     

     主线程的主要作用

     a、显示\刷新UI界面

     b、处理UI事件(比如点击事件、滚动事件、拖拽事件等)

     

     主线程相关用法

     + (NSThread *)mainThread; //获得主线程

     - (BOOL)isMainThread; //是否为主线程

     + (BOOL)isMainThread; //是否为主线程

     

     开发原则:1、比较耗时的操作都放到子线程中(一般是在进行网络请求的时候,或者是执行时间不可控的时候)

             2UI操作、与用户交互的代码都放到主线程中,其一是因为为了保证用户操作的流程性,其二是因为所有的UI控件都在UIKIT框架中,而UIKIT框架采用的就是这种机制,苹果公司也推荐这种用法,都是非线程安全的,为了保证正确,将所有的用户交互的代码放到主线程中,而单一的线程是按顺序执行的,所以就避免了非线程安全的问题

     

     线程内的代码的执行顺序:

        串行执行:同一线程中,该线程中的任务代码按顺序执行,同一时间只能执行一块代码

        并发执行:一个进程可以创建多个子线程,在不同的线程中,任务同时执行(其实是一种假象,是因为CPU的调度速度非常快速,所以感觉是一起执行的)(即多线程)

        并行执行:真正的同时执行,由多个CPU同时执行

     

     多线程的优点

     a、能适当提高程序的执行效率

     b、能适当提高资源利用率(CPU、内存利用率)

     

     多线程的缺点

     a、开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能

     b、线程越多,CPU在调度线程上的开销就越大

     c'程序设计更加复杂:比如线程之间的通信、多线程的数据共享

      

     线程安全:保证多条线程进行读写操作,都能够得到正确的结果!

     使用线程同步技术

     解决方案:互斥锁

     优点:能有效防止因多线程抢夺资源而引起的数据安全问题!

     缺点:需要消耗大量的CPU资源!

     原理:多条线程在同一条线上按顺序执行任务!

     

     iOS中多线程的实现方案

     方案1pthreadC

     一套通用的多线程API、适用于Unix\Linux\Windows等系统、跨平台\可移植、使用难度大

    //创建线程:返回值: 0 == 创建成功,!= 0 返回的是失败的状态码    // pthread_t *restrict :线程标识符的地址,标识一条线程。    // const pthread_attr_t *restrict: 给线程设置属性。    // <#void *(*)(void *)#>:   要执行的方法    // <#void *restrict#> : 传递给执行函数的参数    int result = pthread_create(&mythread, NULL, longTimeOperation, str);

    

     方案2NSThreadOC

     使用更加面向对象、简单易用,可直接操作线程对象

     

创建、启动线程,initWithTarget:在哪里执行,object:参数     NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];          就会告诉 CPU 准备就绪,可以随时接受 CPU 调度! CPU 调度当前线程之后,就会在线程thread中执行self的run方法     [thread start];     NSThread *current = [NSThread currentThread];          线程的调度优先级     + (double)threadPriority;     + (BOOL)setThreadPriority:(double)p;     - (double)threadPriority;     - (BOOL)setThreadPriority:(double)p;     调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高          线程的名字     - (void)setName:(NSString *)n;     - (NSString *)name;          第二种创建方式:创建线程后自动启动线程     [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];          第三种创建方式:隐式创建并启动线程     [self performSelectorInBackground:@selector(run) withObject:nil];          上述2种创建线程方式的优缺点     优点:简单快捷     缺点:无法对线程进行更详细的设置     阻塞(暂停)线程     + (void)sleepUntilDate:(NSDate *)date;     + (void)sleepForTimeInterval:(NSTimeInterval)ti;     // 进入阻塞状态          强制停止线程     + (void)exit;     // 进入死亡状态

<span style="font-size:18px;">/*     线程状态     线程池:存放线程的池子! 分为:     可调度线程池: CPU 只会调度可调度线程池中的线程! 下面蓝色状态都位于可调度线程池中! '就绪' ,'运行'!     不可调度线程池: 下面红色状态都位于不可调度线程池中! "新建" ,"阻塞" ,"死亡"!          线程状态:     start            CPU调度当前线程           运行结束/强制退出(exit)     "新建" ---------->'就绪' -----------------> '运行' -----------------------> "死亡";          CPU 调度其他线程           CPU调度当前线程     '运行' ------------------> '就绪'-----------------> '运行'          调用 sleep/等待互斥锁            sleep时间到/得到互斥锁     '运行' -----------------------> "阻塞"-----------------------> '就绪';          线程运行结束或者强制退出(exit)就进入 "死亡" 状态;     "注意:一旦线程停止(死亡),就不可以再次开启任务!程序会挂掉: Crash!     面试语句: 平时开发中,要特别关注 Crash! :"PO"级别的 "Bug";     注意:一旦线程停止(死亡)了,就不能再次开启任务          线程间通信     什么叫做线程间通信     在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信          线程间通信的体现     1个线程传递数据给另1个线程     在1个线程中执行完特定任务后,转到另1个线程继续执行任务          线程间通信常用方法     - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;     - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;     */</span><pre name="code" class="html"><span style="white-space:pre"></span>
     <pre name="code" class="html">//屏幕点击的时候触发,线程间的通信演示-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{        NSLog(@"touchesBegan:%@",[NSThread currentThread]);    //创建一个线程对象    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(didDownLoadImage) object:nil];        //设置线程名称,一般用于调试    thread.name = @"我是自定义的子线程";        //设置线程大小    //thread.stackSize = 1024 * 1;        //将线程加入到可执行线程池中,CPU随时可以调用    [thread start];}//线程方法-(void)didDownLoadImage{        NSLog(@"didDownLoadImage:%@",[NSThread currentThread]);        //网络资源唯一标示符,    NSURL *url = [[NSURL alloc] initWithString:@"http://e.hiphotos.baidu.com/image/pic/item/11385343fbf2b211e7d75bf9cf8065380dd78e88.jpg"];        //获取本地图片    //NSURL *url = [[NSURL alloc] initWithString:@"file:////Users/mac/Desktop/img01.png"];        //将路径转换为二进制对象,获取网络中的资源    NSData *data = [NSData dataWithContentsOfURL:url];        //将图片保存到本地    [data writeToFile:@"/Users/mac/Desktop/多线程/image/mv01.jpg" atomically:YES];        //将二进制文件转换为图片    UIImage *image = [UIImage imageWithData:data];        //图片下载成功后,返回主线程显示图片    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];}//显示图片-(void)showImage:(UIImage*)image{        NSLog(@"showImage:%@",[NSThread currentThread]);    //设置图片    self.gsImage.image = image;}

<span style="font-size:18px;">////  ViewController.m//  可变化的宫格////  Created by mac on 15/9/1.//  Copyright (c) 2015年 superMac. All rights reserved.//#import "ViewController.h"#define magin 4#define count 20#define cell 4#define row (count / cell + (count % cell > 0 ? 1 : 0))#define gsScreenWidth [[UIScreen mainScreen] bounds].size.width#define gsScreenHeight [[UIScreen mainScreen] bounds].size.height#define gsHeight (gsScreenWidth - (magin * (row + 1))) / row + 50#define gsWidth (gsScreenWidth - (magin * (cell + 1))) / cell@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];        [self showImage];}-(void)showImage{        for (int i = 0; i < count; i++) {        //行的下标        CGFloat rowIndex = i / cell;        //列的下标        CGFloat cellIndex = i % cell;                //x        CGFloat x = (magin + gsWidth ) * cellIndex + magin;        //y        CGFloat y = (magin + gsHeight) * rowIndex + magin;                //创建imageView        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mv01.jpg"]];               imageView.frame = CGRectMake(x, y, gsWidth, gsHeight);                [self.view addSubview:imageView];    }}@end</span>


     方案3GCDC

     旨在替代NSThread等线程技术、充分利用设备的多核


     方案4NSOperationOC

     基于GCD(底层是GCD)、比GCD多了一些更简单实用的功能、使用更加面向对象


     补充

     添加桥接的目的

     凡是函数名中带有create\copy\new\retain等字眼,都应该在不需要使用这个数据的时候进行release

     GCD的数据类型在ARC环境下不需要再做release

     CF(Core Foundation)的数据类型在ARC\MRC环境下都需要再做release


        

     安全隐患

     资源共享

     1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源

     比如多个线程访问同一个对象、同一个变量、同一个文件

     当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题


     安全隐患解决互斥锁

     互斥锁使用格式

     @synchronized(锁对象) { //需要锁定的代码  }

     注意:锁定1份代码只用1把锁,用多把锁是无效的

     

     互斥锁的优缺点

     优点:能有效防止因多线程抢夺资源造成的数据安全问题

     缺点:需要消耗大量的CPU资源

     

     互斥锁的使用前提:多条线程抢夺同一块资源

     

     相关专业术语:线程同步

     线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)

     互斥锁,就是使用了线程同步技术

     

     原子和非原子属性

     OC在定义属性时有nonatomicatomic两种选择

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

     nonatomic:非原子属性,不会为setter方法加锁

     

     nonatomicatomic对比

     atomic:线程安全,需要消耗大量的资源

     nonatomic:非线程安全,适合内存小的移动设备

     

     iOS开发的建议

     所有属性都声明为nonatomic

     尽量避免多线程抢夺同一块资源

     尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

     C语言中的 void * 就等同于 OC 中的 id;  

     -fobjc-arc :可以让旧项目支持arc

     -fno-objc-arc :让原来支持arc的文件不使用arc

0 0
原创粉丝点击