• 多线程理解


    网络操作比较耗时,如果网络操作没有执行完毕,用户的其他操作就会被阻塞,用户感觉非常卡顿.体验不好.所以多线程是专门解决这种问题的.

    单线程

    1.操作内存的栈空间 , 速度非常快

    1. 操作内存的常量区 , 速度比较快(比操作栈区稍微慢点) 3.操作内存的堆空间 , 速度有点慢,比操作常量区慢,循环非常消耗CPU资源 4.使用@""定义的字符串保存在常量区

    I/O操作 : 速度非常慢,引入多线程后,不会造成程序卡顿.

    小结: (1) 耗时操作的后果:如果只有一条线程,会造成程序卡顿,用户体验极差。 (2) 学习多线程的目的:将耗时操作放到后台线程去执行,从而避免卡住主线程。 (3) 通过耗时操作演练可知,操作效率的顺序: I/O操作 < 堆区 < 常量区 < 栈区。 (4) 使用@””定义的字符串保存在常量区,使用stringWithFormat拼接的字符串保存在堆区。 (5) 网络操作也属于耗时操作,通过多线程技术可以将耗时的网络操作放到后台线程去执行,从而提高程序执行效率,改善用户体验。 (6) - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg 在后台线程执行某方法。

    思考: (1)耗时操作会对我们的应用程序产生什么影响? 耗时操作的后果:在主线程,耗时操作会造成程序卡顿,用户会以为程序死了,用户体验极差。

    (2)耗时操作造成的程序卡顿问题该怎么解决? 要想解决程序卡顿问题,就需要使用多线程技术,将耗时操作放到子线程去执行。

    多线程的基本概念:

    1.同步

    (1)同步的概念:必须等待当前语句执行完毕,才可以执行下一个语句。

    1.简单讲,同步就是指:代码从上往下一行一行按顺序执行。

    2.异步

    (2)异步的概念:不用等待当前语句执行完毕,就可以执行下一个语句。

    2.简单讲,异步就是指:代码不一定按照从上往下的顺序执行,可以同时执行。

    3.进程和线程的概念

    3.1 进程

    • 进程的概念:系统中正在运行的应用程序。
    • 进程的特点:每个进程都运行在其专用且受保护的内存空间,不同的进程之间相互独立,互不干扰。

    3.2 线程

    1> 线程的概念:

    • 一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程)。
    • 线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行的。

    2> 线程的串行特点: 一条线程在执行任务的时候是串行(按顺序执行)的。

    - 如果要让一条线程执行多个任务,那么只能一个一个地按顺序执行这些任务。
    - 也就是说,在同一时间,一条线程只能执行一个任务。

    4.多线的概念及原理

    什么是多线程? 1个进程可以开启多条线程,多条线程可以并发(同时)执行不同的任务。

    多线程技术可以提高程序的执行效率。

    4.2多线程的原理 首先,前提是单核CPU。

    1> 同一时间,CPU只能处理一条线程,即同一时间,只有一条线程在工作(执行)。 2> 多线程并发(同时)执行,其实是CPU快速地在多条线程之间进行调度(切换)。 3> 如果CPU调度线程的速度足够快,就会造成多线程并发执行的”假象”。

    思考:如果线程开得非常多,会出现什么情况?

    • CPU会在N多条线程之间调度,会消耗大量CPU资源
    • 每条线程被调度的频次会降低(线程的执行效率降低)

    多线程的优缺点

    4.1 优点

    1> 能适当提高程序的执行效率 2> 能适当提高资源的利用率(CPU、内存利用率)

    4.2 缺点

    1> 开启线程需要占用一定的内存空间(默认情况下,每一条线程都占用512KB),如果开启大量的线程,会占用大量的内存空间,从而降低程序的性能。 2> 线程越多,CPU在调度线程上的开销就越大。 3> 线程越多,程序设计就会更复杂:比如 线程间通讯、多线程的数据共享等。

    总结 : 开线程,必然就会消耗性能,却可以提高用户体验,在保证良好的用户体验的前提下,可以适当地开线程,一般开3-6条。

    5.主线程

    主线程 : 一个应用程序运行后,系统默认会开启1条线程,称为”主线程”或”UI线程”。

    作用 : 显示 / 刷新 UI 界面 和 处理 UI 事件

    主线程的使用注意 : 1> 不要将比较耗时的操作放到主线程去执行。 2> 耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种”卡”的坏体验。

    //__bridge 桥接 //MRC中内存管理原则:谁申请,谁释放 //ARC中会自动给OC对象,添加retain release autorelease //把OC中的对象,传递给c语言的函数,要桥接

    在MRC开发中,不需要进行桥接。

    NSThread

    创建线程的3种方法

    1.1 第一种方法: alloc initWithTarget

    小结: (1)对象方法:- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument; (2)方法的作用:创建一个线程对象,执行 Target 的 selector 方法,并且可以给 selector 方法传递一个 object 参数。 (3)参数介绍: 1> Target:表示提供 selector 方法的对象,大多数情况(99%)是 self,但是也有可能不是self。 注意: 这里的 Target 并不一定是self,主要要看 selector 方法是谁的方法,是谁的方法,Target就写谁。 2> selector:交给线程执行的方法 3> object:传递给selector方法的参数。这个例子中,没有传递参数(object是nil)。 (4)通过 alloc initWithTarget 方法创建线程对象,需要调用 start 方法来启动线程。

    1.2第二种方法: detach 方法

    小结:

    (1) 类方法:

    • (void)detachNewThreadSelector:(SEL)selector toTarget: (id)target withObject:(nullable id)argument; (2) 方法的作用: 创建并启动线程(实例化一个线程对象之后直接启动线程) (3) 方法的参数: 1> Selector: 交给线程执行的方法 2> Target: 提供selector方法的对象,通常是self 3> Object: 要传递给 Selector 方法的参数 (4) 通过 detach 类方法创建线程,不需要调用 start 方法来启动线程,因为这个方法的底层会自动调用 start 方法。

    1.3 第三种方法: perform方法

    小结: (1) NSObject分类方法:

    • (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg; (2) 方法的作用: 隐式(没有任何跟线程相关的字眼)创建并启动线程 (3) 参数介绍: 1> selector: 交给线程执行的方法 2> Object:要传递给 selector 方法的参数 3> 注意:这里的 selector 方法由 perform 方法的调用者来提供,这个例子中 demo 方法是由当前控制器对象提供的,即self。

    既然是NSObject的分类方法,就说明:任何继承自NSObject的对象都可以直接调用这个方法。

    1.5 总结:

    (1) 创建线程的方法: pthread_create/alloc initWithTarget/detach/perform。

    • 实际上,通过NSThread创建线程一共两种方法: initWithTarget 和 detach。
    • perform方法是NSObject的分类方法。
    • 如果算上pthread(仅作了解),到目前为止,一共学习了4种创建线程的方法。

    (2) 使用detach 和 perform这两种方法创建线程更加便利,其中perform方法使用起来更灵活。 (3) detach 和 perform 方法无法对线程进行更详细的设置,如果要对线程进行详细设置,只能使用alloc initWithTarget方法来创建。

    3> 线程状态

    NSTread 必须开启 , 才能运行

    • 运行(Running): 当CPU调度到当前线程的时候,当前线程就是运行状态。

    注意:线程执行完成之前,会一直在就绪状态和运行状态之间来回切换。并且这种切换是由CPU负责的,程序员不能干预。

    • 阻塞(Blocked): 当满足某个预定条件时,可以使用休眠或者线程同步来阻塞线程的执行。
    • 休眠(两个类方法)

    • (void)sleepUntilDate:(NSDate *)date; // 让线程休眠到指定时间
    • (void)sleepForTimeInterval:(NSTimeInterval)ti; // 让线程休眠一段时间

    4.线程同步(互斥锁) @synchronized(self) { // 代码; }

    注意:当线程对象进入阻塞状态之后,会被从”可调度线程池”中移出,这样的话,CPU就不会调度它。因为CPU只会调度”可调度线程池”中的线程对象。

    5.死亡(Dead):

    • 正常死亡:

    线程对象将指定方法中的所有代码执行完毕,并且线程对象没有被复用,就会正常死亡。

    • 非正常死亡:

    调用 exit 方法,可以强行终止当前线程。线程被终止后,后续的代码就不会再执行。 注意:一定不要在主线程调用 exit 方法,主线程要是死了,程序就挂掉了。 一旦线程死亡,就不能再次被使用,只能重新创建线程。”线程死不能复生”

    线程属性:

    // 设置线程的名称 thread1.name = @"A";

    小结:

    (1) 含义: 线程的名称。 (2) 作用: 在多线程开发时,可以用来判断到底是哪个线程在执行指定的任务。 在企业级开发中,当程序发生崩溃的时候,可以帮助我们快速定位问题所在。

    // 设置线程的优先级 thread1.threadPriority = 1.0;

    小结:

    (1) 取值: 取值范围 0.0 – 1.0,默认是 0.5,1.0表示优先级最高 。 (2) 使用注意: 优先级高,只表示当前线程被CPU调度的频率相对较高。并不表示CPU会先调度优先级高的线程执行完它的所有任务,才会去调度优先级低的线程去执行任务。

    多线程共享资源

    (1) 资源共享 1块资源可能被多个线程共享,也就是多个线程可能会访问同一块资源。 比如:多个线程访问同一个对象、同一个变量、同一个文件。 (2) 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

    线程隐患分析

    当线程A和线程B同时对Integer类型的变量进行读写操作:

    1.线程A读取到的值:17; 线程B读取到的值:17。 2.线程A对变量做+1操作,然后将结果18重新写入。 3.线程B也对变量进行+1操作,然后将结果18重新写入。

    这个时候就出问题了,做了两次+1操作,结果仍然是18,显然是不对的。

    分析解决方案

    互斥锁

    (2) 互斥锁的格式: @synchronized(锁对象) { // 需要锁定的代码 } 在Xcode7之前,敲互斥锁的代码没有智能提示。 (3) 互斥锁的优缺点 1> 优点: 能有效防止因多线程抢夺资源造成的数据安全问题 2> 缺点: 会消耗大量的CPU资源,影响程序的执行效率 (4) 相关专业术语: 线程同步 1> 线程同步的意思是: 当多条线程要访问同一块资源的时候,必须等待前一条线程访问完毕,后一条线程才可以访问。 2> 互斥锁,就是使用了线程同步技术。 (5) 使用注意 1> 互斥锁中的锁对象必须是一个全局的锁对象。 2> 互斥锁锁定的代码范围应该尽可能小,锁住读写部分的代码即可。 思考: 如果锁住整个while(YES)死循环,会产生什么后果?

    互斥锁 和自旋锁的区别:

    (1) 相同点: 都能够保证锁定范围的代码,同一时间,只有一条线程执行。 (2) 不同点:

    1> 互斥锁 如果发现其它线程正在执行锁定代码,线程会进入休眠(阻塞状态),等其它线程时间片到了打开锁后,线程就会被唤醒(执行)。 2> 自旋锁 如果发现有其它线程正在执行锁定代码,线程会以死循环的方式,一直判断锁定的代码是否解锁,一旦解锁,立即执行。 自旋锁更适合执行不耗时的代码。

    在 info.plist 中 添加

    NSAppTransportSecurity

         <dict>
         <key>NSAllowsArbitraryLoads</key>
         <true>

    线程间通信

    (1)什么叫做线程间通讯? 在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信。 小秘买完烟,必须得回来给班长汇报一下。

    (2)线程间通信的体现 1.1个线程传递数据给另一个线程 2.在一个线程中执行完特定任务后,转到另一个线程继续执行任务

    (3) 线程间通信常用方法 -(void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

    • (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
  • 相关阅读:
    WordPress网站绑定多个域名的方法
    htpasswd 命令使用
    在Windows下用OpenSSL生成证书步骤
    WCF中关于List和数据的转换问题
    NET2.0的配置文件
    C# Attribute
    c#自定义属性
    VS2005中读写配置文件(方法二)
    c#的反射
    Asp.NET 操作配置文件 Steven Pei 博客园
  • 原文地址:https://www.cnblogs.com/graveliang/p/5682233.html
Copyright © 2020-2023  润新知