美文网首页
基础知识总结(二)

基础知识总结(二)

作者: Sunrain16 | 来源:发表于2017-06-22 12:00 被阅读352次

    昨天忙碌了一天没有继续做自己的笔记,今天继续。今天主要是加深一下自己对GCD、NSOperation、NSthread的理解和使用吧。

    1.进程和线程的区别?同步异步的区别?并行和并发的区别?

    进程、线程的叫法是相对的。每一个应用运行的进程相对于一台电脑、一个手机来说是线程。但对于一个应用来说,我们会在应用的进程里边开辟很多线程,来处理不同的耗时的任务。

    进程:有独立的地址空间,一个进程崩溃之后,在保护模式下不会对其他进程产生影响。进程是资源分配的基本单位。

    线程:进程中的任务的不同执行路径。线程有自己的堆栈和局部变量,但是没有单独的地址空间。一个线程死掉等同于整个进程死掉。所以多进程的程序比多线程的程序健壮。但是进程之间的切换,资源消耗较大。效率低。

    联系:二者都是由操作系统所体会的程序运行的基本单元。系统利用该基本单元实现系统对应用的并发性。进程是线程的容器,代码执行在线程中,进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程。一个进程中的所有线程共享当前进程所拥有的资源。

    同步:在程序内的调用者发出一个“调用”之后,在没有得到调用结果(CPU返回执行的数据段部分)之前,调用者会一直处于等待的状态,直至结果返回,才会执行下一个语句。换句话说,就是由调用者主动等待这个“调用”的结果。并不会耽误CPU处理其他的事件。

    异步:调用者发出“调用”之后,调用会直接返回。没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在“调用”发出后,“被调用者”通过状态、通知来通知调用者,或通过回调函数处理这个调用。

    并行:CPU的多核芯同时执行多个任务。

    并发:单核CPU交替执行两个任务。

    2,线程间通信?

    线程间通讯的体现:

    (1)一个线程传递数据给另一个线程,如:网络数据的异步线程请求,回归主线程进行赋值,刷新UI。

    (2)在一个线程中执行完特定任务之后,转到另一个线程继续执行任务。

    线程间通信常用的方法:

    (1)NSThread`可以先将自己的当前线程对象注册到某个全局的对象中去,这样相互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在主线程执行的方法。

    -(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 NS_AVAILABLE(10_5,2_0);

    (2)GCD,一个线程的数据传递给另一个线程。

    - (void)touchesBegan:(NSSet*)touches withEvent(UIEvent*)event

    {

              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

              NSLog(@"donwload---%@", [NSThreadcurrentThread]);// 1.子线程下载图片

              NSURL*url = [NSURLURLWithString:@"http://d.jpg"];

              NSData*data = [NSDatadataWithContentsOfURL:url];

              UIImage*image = [UIImageimageWithData:data];

              // 2.回到主线程设置图片

              dispatch_async(dispatch_get_main_queue(), ^{

                        NSLog(@"setting---%@ %@", [NSThreadcurrentThread], image);

                       [self.button setImage:image forState:UIControlStateNormal];

              });

         });

    }

    3.GCD的一些常用的函数?(group,barrier,信号量,线程同步)

    延迟执行任务函数:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    <#code to be executed after a specified delay#>

    });

    只执行一次的函数

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    <#code to be executed once#>

    });

    栅栏函数

    dispatch_barrier_sync 同步函数,会阻塞线程

    dispatch_barrier_async 异步函数,不会阻塞线程

    如果所有任务都在同一个并行队列中,并且这个并行队列不是系统自带的全局并行队列,那么在barrier之前添加的任务执行完毕之后才会执行barrier任务。只有barrier任务执行完毕之后才会执行barrier之后添加的任务。

    dispatch_barrier_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

    dispatch_barrier_async(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

    队列的使用

    dispatch_group_async(<#dispatch_group_t  _Nonnull group#>, <#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

    定时器

    /*

    第一个参数:给那个定时器设置

    第二个参数:什么时候启动

    第三个参数:间隔多久执行一次

    第四个参数:设置精准度:0 代表最高精准(尽量让定时器精准), 大于0的的话代表是在多少秒内接受.

    第四个参数存在意义:主要是为了提高程序性能, 设置越大,能减轻CPU的压力

    注意:GCD定时器传入的时间都是纳秒

    */

    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, <#dispatchQueue#>);

    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, <#intervalInSeconds#> * NSEC_PER_SEC, <#leewayInSeconds#> * NSEC_PER_SEC);

    dispatch_source_set_event_handler(timer, ^{

    <#code to be executed when timer fires#>

    });

    dispatch_resume(timer);

    信号量

    假设现在系统由两个空闲资源可以被利用,但是同一时间却有三个线程进行访问。这个时候就可以方便的利用信号量来解决。

    信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问资源之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。类似锁机制。

    函数:

    //创建信号量,参数:信号量的初值,如果小于0则会返回NULL

    dispatch_semaphore_create(信号量值)

    //等待降低信号量

    dispatch_semaphore_wait(信号量,等待时间)

    //提高信号量

    dispatch_semaphore_signal(信号量)

    注:正常的使用顺序是先降低然后再提高。两个函数通畅是成对使用。

    代码示例:

    GCD信号量的使用

    4.如何使用队列来避免资源抢夺?

    将需要访问同一块资源的任务添加到一个串行队列执行。队列内部可根据需求来决定是否需要异步或者同步。

    如果不使用队列,可以添加线程锁。有以下方法:

    (1)@synchronized(id anObject)-->最简单的方法

    会自动对参数对象进行加锁,保证临界区内的代码线程安全。

    @synchronized (<#token#>) {

    // 这段代码对其他 @synchronized(self) 都是互斥的

    // self 指向同一个对象

    <#statements#>

    }

    截图1

    (2)采用NSLock

    NSLock对象实现了NSLocking protocol,包含几个方法:

    lock 加锁

    unlock 解锁

    tryLock 尝试请求一个锁,如果失败了,并不会阻塞线程,只是立即返回NO。

    lockBeforeDate,在指定的date之前暂时性阻塞线程(如果没有获取锁的话)。如果到期还没有获取锁,线程被唤醒,函数立即返回NO。

    (3)NSRecursiveLock,递归锁

    NSRecursiveLock,多次调用不会阻塞已获取该锁的线程。

    如图这段代码是一个死锁状况。

    截图2-死锁 截图3-死锁

    替换为NSRecursiveLock试一下,运行结果如图:

    截图4-NSRecursiveLock

    (4)NSConditionLock,条件锁

    NSConditionLock,条件锁,可以设置条件

    [lock lockWhenCondition:A条件];

    /*表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它不会得到改锁,等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,执行代码。同时设置它获得该锁,其他线程都将等待它的代码区执行完毕,直至解锁。*/

    [lock unlockWithCondition:A条件];

    /*表示解锁,同时把内部的condition设置为A条件*/

    (5)NSDistributedLock,分布锁

    NSDistributedLock,分布锁,文件方式实现。可以跨进程。用tryLock方法获取锁。用unLock方法释放锁。如果一个获取锁的进程,在释放锁之前挂了。那么锁就一直得不到释放,此时可以通过breakLock强行解锁。

    (6)使用信号量,限制访问同一块资源的线程数目

    相关文章

      网友评论

          本文标题:基础知识总结(二)

          本文链接:https://www.haomeiwen.com/subject/tengqxtx.html