磨人的妖精

导航

多线程网络总结

一、多线程

1.线程:一个程序就是一个进程,一个进程想要执行任务,必须要有线程,线程是进程执行的基本单元,一个进程的所有任务都在线程上执行。

2.多线程:一个进程可以开启多个线程,每个线程可以并行执行不同的任务。

3.多线程原理:同一个时间,CPU职能执行一条线程,多线程同时执行,其实是CPU在快速的在多条线程之间切换,就造成了多线程的假想。

4.多线程优缺点:

    优点:提高程序执行的效率,能适当提高资源的利用率。

    缺点:开启线程需要占用一定的内存空间,如果开启大量的线程,会占用大量的空间。线程越多,CPU在调度线程上开销就越大。会使程序设计更加复杂,比如多线程之间的通信,多线程的数据共享。

5.线程的作用:更新UI界面(主线程),处理用户触摸事件。

6.开发中实现多线程的方案:

    (1)C语言pthread:一套纯C语言的通用API,且线程的生命周期需要程序员自己管理,使用难度较大,通常不使用。

        特点:POSIX线程具有很好的可移植性,使用pthreads编写的代码可运行于Solaris、FreeBSD、Linux 等平台,Windows平台亦有pthreads-win32可供使用。Pthreads定义了一套C语言的,它以pthread.h头文件和一个线程库实现。

       数据类型:

            pthread_t:线程ID

            pthread_attr_t:线程属性
       操纵函数:
           pthread_create():创建一个线程
           pthread_exit():终止当前线程
           pthread_cancel():中断另外一个线程的运行
           pthread_join():阻塞当前的线程,直到另外一个线程运行结束
           pthread_attr_init():初始化线程的属性
           pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
           pthread_attr_getdetachstate():获取脱离状态的属性
           pthread_attr_destroy():删除线程的属性
           pthread_kill():向线程发送一个信号
       同步函数:
           用于 mutex 和条件变量
           pthread_mutex_init() 初始化互斥锁
           pthread_mutex_destroy() 删除互斥锁
           pthread_mutex_lock():占有互斥锁(阻塞操作)
           pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即,当互斥锁空闲时,将占有该锁;否则,立即返回。
           pthread_mutex_unlock(): 释放互斥锁
           pthread_cond_init():初始化条件变量
           pthread_cond_destroy():销毁条件变量
           pthread_cond_signal(): 唤醒第一个调用pthread_cond_wait()而进入睡眠的线程
           pthread_cond_wait(): 等待条件变量的特殊条件发生
           Thread-local storage(或者以Pthreads术语,称作线程特有数据):
           pthread_key_create(): 分配用于标识进程中线程特定数据的键
           pthread_setspecific(): 为指定线程特定数据键设置线程特定绑定
           pthread_getspecific(): 获取调用线程的键绑定,并将该绑定存储在 value 指向的位置中
           pthread_key_delete(): 销毁现有线程特定数据键
           pthread_attr_getschedparam();获取线程优先级
           pthread_attr_setschedparam();设置线程优先级
       工具函数:
           pthread_equal(): 对两个线程的线程标识号进行比较
           pthread_detach(): 分离线程
           pthread_self(): 查询线程自身线程标识号

    (2)OC的NSThread

       特点:

          优点:NSThread较轻量级,更直观地控制线程对象

          缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

       初始化:

          1.动态方法

          - (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument; 

         // 初始化线程

         NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

         // 设置线程的优先级(0.0 - 1.0,1.0最高级)

         thread.threadPriority = 1;

         // 开启线程

         [thread start];

         //参数解析:

         selector :线程执行的方法,这个selector最多只能接收一个参数

         target :selector消息发送的对象

         argument : 传给selector的唯一参数,也可以是nil

         2.静态方法

         + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;  

         [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  // 调用完毕后,会马上创建并开启新线程

         3.隐式创建线程

          [self performSelectorInBackground:@selector(run) withObject:nil]; 

      获取当前线程:

          NSThread *current = [NSThread currentThread];  

      获取主线程:

          NSThread *main = [NSThread mainThread]; 

      暂停当前线程:

          // 暂停2s  

          [NSThread sleepForTimeInterval:2];  

         // 或者    

          NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];  

          [NSThread sleepUntilDate:date];

      线程间通信:

          1.在指定线程上执行操作          

          [self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];  

          2.在主线程上执行操作

          [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];

          3.在当前线程执行操作

          [self performSelector:@selector(run) withObject:nil];  

    (3)C语言的GCD接口(性能最好,代码更精简)

     特点:

           (1)GCD存在于libdispatch.dylib这个库中,这个调度库包含了GCD的所有的东西,任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入。

           (2)GCD是纯C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法。

           (3)GCD中的函数大多数都以dispatch开头。

     任务和队列:

           GCD中有2个核心概念:(1)任务:执行什么操作     (2)队列:用来存放任务

           GCD的使用就2个步骤(1)定制任务    (2)确定想做的事情

           将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行

           提示:任务的取出遵循队列的FIFO原则:先进先出,后进后出

     执行任务:

          1.GCD中有2个用来执行任务的函数

          说明:把右边的参数(任务)提交给左边的参数(队列)进行执行。

        (1)用同步的方式执行任务 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

         参数说明:

         queue:队列

         block:任务

       (2)用异步的方式执行任务 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

         2.同步和异步的区别

         同步:在当前线程中执行

         异步:在另一条线程中执行

     队列:

         1.队列的类型

         GCD的队列可以分为2大类型

         (1)并发队列(Concurrent Dispatch Queue)

         可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效

         (2)串行队列(Serial Dispatch Queue)

         让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

         2.补充说明

         有4个术语比较容易混淆:同步、异步、并发、串行

         同步和异步决定了要不要开启新的线程

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

         异步:在新的线程中执行任务,具备开启新线程的能力

         并发和串行决定了任务的执行方式

         并发:多个任务并发(同时)执行

         串行:一个任务执行完毕后,再执行下一个任务

         3.串行队列

         GCD中获得串行有2种途径

         (1)使用dispatch_queue_create函数创建串行队列

         dispatch_queue_t dispatch_queue_create(const char *label,  dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL即可

         示例:

         dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 创建

         dispatch_release(queue); // 非ARC需要释放手动创建的队列

         (2)使用主队列(跟主线程相关联的队列)

         主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行

         使用dispatch_get_main_queue()获得主队列

         示例:

         dispatch_queue_t queue = dispatch_get_main_queue();

         4.并发队列

         GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建

         使用dispatch_get_global_queue函数获得全局的并发队列

         dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此参数暂时无用,用0即可

         示例:

         这个参数是留给以后用的,暂时用不上,传个0。

         第一个参数为优先级,这里选择默认的。获取一个全局的默认优先级的并发队列。

         dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

         说明:全局并发队列的优先级

         #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高

         #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)

         #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低

         #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

         5.各种队列的执行效果

        

     线程间通信:

         //1)GCD:

         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

             // 下载图片

             UIImage *image = nil;

             dispatch_async(dispatch_get_main_queue(), ^{

                 // 回到主线程

             });

          });

    (4)OC的NSOperation和NSOperationQueue(基于GCD)

     简介:

         NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现多线程编程

         NSOperation和NSOperationQueue实现多线程的具体步骤:

         (1)先将需要执行的操作封装到一个NSOperation对象中

         (2)然后将NSOperation对象添加到NSOperationQueue中

         (3)系统会⾃动将NSOperationQueue中的NSOperation取出来

         (4)将取出的NSOperation封装的操作放到⼀条新线程中执⾏

     子类:

         NSOperation是个抽象类,并不具备封装操作的能力,必须使⽤它的子类

         使用NSOperation⼦类的方式有3种:

         (1)   NSInvocationOperation

         NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];

          //执行操作

          [operation start];

         操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程。即默认情况下,如果操作没有放到队列中queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作 。

         (2)   NSBlockOperation

         NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{

         //......

          }];

         //添加操作

          [operation addExecutionBlock:^{

               //....

         }];

         //执行操作

          [operation start];       

         只要NSBlockOperation封装的操作数 > 1,就会异步执行操作。

         (3)   自定义子类继承NSOperation,实现内部相应的⽅法

         3. NSOperationQueue的作⽤:NSOperation可以调⽤start⽅法来执⾏任务,但默认是同步执行的

         如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

         添加操作到NSOperationQueue中,自动执行操作,自动开启线程。

     线程间通信:

         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

                 // 下载图片

                 UIImage *image = nil;

                 [self performSelector:@selector(settingImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES modes:nil];

             }    

         //这种情况 也适用于子线程之间的通信。

7.NSOperationQueue和GCD的区别

     1. GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装

     2. GCD只支持FIFO的队列,NSOperationQueue可以很方便地调整执行顺序、设置最大并发数量

     3. NSOperationQueue可以轻松在Operation间设置依赖关系,而GCD需要写很多的代码才能实现

     4. NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)

     5. GCD的执行速度比NSOperationQueue快

     6.使用选择:

         任务之间不太互相依赖:GCD

         任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue

8.多线程安全解决方案

     1. 只在主线程刷新访问UI

     2. 如果要防止资源抢夺,得用synchronized进行加锁保护

     互斥锁使用格式:@synchronized(锁对象) { // 需要锁定的代码  }注意:锁定1份代码只用1把锁,用多把锁是无效的。

     互斥锁的优缺点

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

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

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

     相关专业术语:线程同步,多条线程按顺序地执行任务,互斥锁,就是使用了线程同步技术。

     3. 如果异步操作要保证线程安全等问题, 尽量使用GCD(有些函数默认就是安全的)

     OC在定义属性时有nonatomic和atomic两种选择

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

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

     原子和非原子属性的选择

     nonatomic和atomic对比

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

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

     iOS开发的建议

     所有属性都声明为nonatomic

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

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

二、网络

 

posted on 2016-09-12 09:46  磨人的妖精  阅读(251)  评论(0编辑  收藏  举报