iOS_多线程理解

1.基本概念:

什么是进程:

1)进程是一个具有独立功能的程序关于某次数据集合的一次运行活动,他是操作系统分配资源的基本单元.

2) 进程是指在系统中正在运行的一个应用程序,就是一段程序执行的过程,我们可以理解为手机上的一个app

3)每个进程之间是独立的,每个进程均运行在专用且受保护的内存空间内,拥有独立运行,所需的全部资源

什么是线程

1)程序执行流的最小单元,线程是进程中的一个实体

2)一个进程要想执行任务,必须要至少有一条线程,应用启动的时候,系统会默认开启一条线程,也就是主线程;

线程和进程之间的关系

1)线程是进程的基本执行单位,进程的所有任务必须在线程中执行(主线程,子线程)

2)线程是cpu分配资源的最小单元,一个进程中所有线程共享进程资源

3)一个进程对应多个或者一个线程,但是必须要有线程

队列:这里的队列是指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO的原则,即新任务是被插入到队列末尾,读取总是从头部开始读取,读取一个释放一个

在GCD中有两种队列,串行和并发,遵循FIFO 区别在于自行顺序不同,开启线程数不同

iOS 中的多线程

NSThread,NSoperationQueue,GCD

我们要明确 NSOperationQueue 与 GCD 之间的关系

GCD 是面向底层的 C 语言的 API,NSOpertaionQueue 用 GCD 构建封装的,是 GCD 的高级抽象。

1、GCD 执行效率更高,而且由于队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构,写起 来更方便

2、GCD 只支持 FIFO 的队列,而 NSOperationQueue 可以通过设置最大并发数,设置优先级,添加依赖关系 等调整执行顺序

3、NSOperationQueue 甚至可以跨队列设置依赖关系,但是 GCD 只能通过设置串行队列,或者在队列内添 加 barrier(dispatch_barrier_async)任务,才能控制执行顺序,较为复杂

4、NSOperationQueue 因为面向对象,所以支持 KVO,可以监测 operation 是否正在执行(isExecuted)、 是否结束(isFinished)、是否取消(isCanceld)

  实际项目开发中,很多时候只是会用到异步操作,不会有特别复杂的线程关系管理,所以苹果推崇的 且优化完善、运行快速的 GCD 是首选

  如果考虑异步操作之间的事务性,顺序行,依赖关系,比如多线程并发下载,GCD需要自己写更多的 代码来实现,而 NSOperationQueue 已经内建了这些支持

  不论是GCD还是NSOperationQueue,我们接触的都是任务和队列,都没有直接接触到线程,事实上 线程管理也的确不需要我们操心,系统对于线程的创建,调度管理和释放都做得很好。而 NSThread 需要我们自己去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销


2、performSelector

dispatch_async(dispatch_get_global_queue(0, 0), ^{

        [self performSelector:@selector(threadFucntion) withObject:nil afterDelay:0];

    });

这里的 threadFucntion 方法是不会去执行的,原因在于

这个方法要创建提交任务到 runloop 上的,而 gcd 底层创建的线程是默认没有开启对应 runloop 的,所有 这个方法就会失效。

而如果将 dispatch_get_global_queue 改成主队列,由于主队列所在的主线程是默认开启了 runloop 的, 就会去执行(将 dispatch_async 改成同步,因为同步是在当前线程执行,那么如果当前线程是主线程,test 方法也是会去执行的)。

1、问:怎么用 GCD 实现多读单写?

多读单写的意思就是:可以多个读者同时读取数据,而在读的时候,不能去写入数据。并且,在写的过程 中,不能有其他写者去写。即读者之间是并发的,写者与读者或其他写者是互斥的。

这里的写处理就是通过栅栏的形式去写。 就可以用 dispatch_barrier_sync(栅栏函数)去实现

多读单写实现方式:

-(id)readDataForKey:(NSString*)key{

    __blockidresult;


    //当前线程和取的线程为同一个

    dispatch_queue_t concurrent = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);


    dispatch_sync(concurrent, ^{

        result = [selfvalueForKey:key];

    });

    returnresult;

}

-(void)writeDataForKey:(NSString*)key{


    dispatch_queue_t concurretn = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);


    dispatch_barrier_async(concurretn, ^{

        [selfsetValue:@"data"forKey:key];

    });

}

介绍:dispatch_group_async

场景:在 n 个耗时并发任务都完成后,再去执行接下来的任务。比如,在 n 个网络请求完成后去刷新 UI 页 面。

dispatch_queue_t concurrentQueen = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_t group = dispatch_group_create();

    for(NSIntegeri =0; i <5; i ++ ) {

        dispatch_group_async(group, concurret, ^{

            sleep(1);

            NSLog(@"网络请求 %ld",i);

        });

    }

    dispatch_group_notify(group,concurrentQueen, ^{

        NSLog(@"刷新页面");

    });

介绍:Dispatch Semaphore

GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。

A 常用做,保持线程同步,将异步执行任务转换为同步执行

B 保持线程安全,为线程加锁

__blockNSIntegernumber =0;

    dispatch_semaphore_t semphone = dispatch_semaphore_create(0);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        number =100;

        sleep(10);

        dispatch_semaphore_signal(semphone);

    });

    dispatch_semaphore_wait(semphone, DISPATCH_TIME_FOREVER);


    NSLog(@"321321aaaaaaa13231  %ld",number);

发现 过了10秒钟才打印number = 10 ;

说明阻塞了当前线程,将异步执行,转换为同步了

在线程安全中可以将 dispatch_semaphore_wait 看作加锁,而 dispatch_semaphore_signal 看作解锁 首先创建全局变量

@property (nonatomic,strong)dispatch_semaphore_t semphore;

//为线程加锁

-(void)semPhoreLock{

    dispatch_semaphore_wait(_semphore, DISPATCH_TIME_FOREVER);

    _count++ ;

    sleep(1);

    NSLog(@"sdad  d3232 %d",_count);

    dispatch_semaphore_signal(_semphore);

}

-(void)asyncDic{

    _semphore = dispatch_semaphore_create(1);

    for(inti =0; i <100; i ++ ) {

        dispatch_async(dispatch_get_global_queue(0, 0), ^{

            [selfsemPhoreLock];

        });

    }

}

观察到打印是从1打印100,且dispatch_semaphore_t必须用strong修饰

原因如下:

在子线程中并发执行 asyncTask,那么第一个添加到并发队列里的,会将信号量减 1,此时信号量等于 0, 可以执行接下来的任务。而并发队列中其他任务,由于此时信号量不等于 0,必须等当前正在执行的任务 执行完毕后调用 dispatch_semaphore_signal 将信号量加 1,才可以继续执行接下来的任务,以此类推,从而 达到线程加锁的目的。

延时函数

dispatch_after 

dispatch_after 能让我们添加进队列的任务延时执行,该函数并不是在指定时间后执行处理,而只是在指 定时间追加处理到 dispatch_queue

dispatch_once_tonce 实现单例

-(instancetype)shareInstance{

    static dispatch_once_tonceToken;

    static id instance =nil;

    dispatch_once(&onceToken, ^{

        instance = [[self alloc]init];

    });

    return instance;

}

NSThread+runloop 实现常驻线程


[self performSelector:@selector(threadJJJ) onThread:[ViewController shareThread] withObject:nil waitUntilDone:NO];

调用performselector 这个就实现了打印,说明,实现NSThread + RunRoop 实现成功了

自旋锁:是一种用于保护多线程共享资源的锁,与一般互斥锁(mutex)不同之处在于当自旋锁尝试获取锁时以忙等 待(busy waiting)的形式不断地循环检查锁是否可用。当上一个线程的任务没有执行完毕的时候(被锁住), 那么下一个线程会一直等待(不会睡眠),当上一个线程的任务执行完毕,下一个线程会立即执行。在多 CPU 的环境中,对持有锁较短的程序来说,使用自旋锁代替一般的互斥锁往往能够提高程序的性能。

互斥锁:当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会进入睡眠状态等待任务执行完毕, 当上一个线程的任务执行完毕,下一个线程会自动唤醒然后执行任务。

自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁 资源释放锁。互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时 cpu 可以调度其他线程工 作。直到被锁资源释放锁。此时会唤醒休眠线程。

你可能感兴趣的:(iOS_多线程理解)