您的位置:首页 > 编程语言

多线程编程

2015-10-05 20:33 190 查看
</pre><p>首先要强调的是数组的并行和串行两种执行方式。</p><p></p><pre name="code" class="objc"> NSArray *array = @[@1,@2,@3,@4,@5];

//数组排序:
//NSSortStable:串行,在排序执行的时候,程序为顺序结构执行,排序不执行完成,后期的代码不会运行。(耗时间,但结果安全)
//NSSortConcurrent:并行,在程序执行的时候,程序为多线程方式,排序方法执行的时候,下面的代码也同步执行。(节省时间,结果不安全)
[array sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];

NSLog(@"%@",array);


现在开始步入正题,这节课我们讲的是iOS高级编程中的多线程编程。

程序是什么?

程序是由源代码生成的可执行应用。

进程是什么?

进程:一个正在运行的程序可以看错一个进程。进程拥有独立运行所需的全部资源。

线程是什么?

线程是程序中独立运行的代码段。

程序,进程,线程之间的关系呢?

一个进程是由一个或多个线程组成,进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。

单线程?

每个正在运行的程序(即进程)至少包含一个线程,这个线程叫主线程。

主线程在程序启动时被创建,用于执行main函数。

只有一个主线程的程序,称作单线程程序。

主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行。

多线程?

拥有多个线程的程序,称为多线程程序。

iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程。

可以根据需要开辟若干子线程。

子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行。

单线程和多线程的区别?

单线程程序:只有一个线程,代码顺序执行,容易出现代码阻塞(页面假死)

多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能。

注意:iOS中关于UI的添加和刷新必须在主线程中操作。

iOS平台下得多线程

多线程的种类

1.脱离线程

2.非脱离线程

iOS多线程实现种类

NSObect 实现异步后台执行

NSObject中存在了一个最简单的后台执行的方法

//NSObject 多线程方式 后台执行某个方法
[self performSelectorInBackground:@selector(calculate) withObject:nil];


NSThread

是一个轻量级的多线程,它有以下两种创建方法。

//NSThread 多线程方式

 //方式1 初始化一个子线程,但需要手动开启
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(calculate) object:nil];
[thread1 start]; //开启子线程,concel取消子线程

//方式2 初始化一个子线程并自动开启
[NSThread detachNewThreadSelector:@selector(calculate) toTarget:self withObject:nil];


注意:在多线程方法中需要添加自动释放池

在应用程序打开的时候,系统会自动为主线程创建一个自动释放池

我们手动创建的子线程需要我们手动添加自动释放池!

NSOperationQueue

NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类。

因为它是抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation或NSBlockOperation)来执行实际任务

NSOperation(含子类),只是一个操作,本身无主线程、子线程之分,可在任意线程中使用。通常与NSOperationQueue结合使用。

NSInvocationOperation是NSOperation的子类

封装了执行操作的target和要执行的action

NSBlockOperation是NSOperation的子类

封装了需要执行的代码块

NSOperationQueue是操作队列,他用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行。

其中NSOperation可以调节它在队列中的优先级。

当最大并发数设置为1的时候,能实现线程同步。

//NSOperation/NSOperationQueue 多线程方式

 //方式一NSInvocationOperation
NSInvocationOperation *invocation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printNumber) object:nil];

#warning 需要手动开启 辅助实现多线程
//    [invocation start];

//方式二NSBlockOperation Block会持有内部变量
__weak typeof(self) temp = self;

NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
[temp printAnotherGroupNumber];
}];
//    [block start];

//可以通过NSOperationQueue创建队列,向队列中添加操作,完成多任务同时执行,当最大并发数为1,变成了串行。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
#warning 一定要提前设置好并发数 决定是否能够并发进行 如上所述
[queue setMaxConcurrentOperationCount:1];
[queue addOperation:invocation];
[queue addOperation:block];

- (void)printNumber
{
    for (int i =0; i<10; i++) {
        NSLog(@"1");
    }
}
- (void)printAnotherGroupNumber
{
    for (int i =0; i<10; i++) {
        NSLog(@"2");
    }
}
- (void)calculate{
#warning 一定要写自动释放池
    @autoreleasepool {
        NSArray *array = @[@1,@2,@3,@4,@5];
        
        for (int i = 0; i <10; i++) {
            array = [array arrayByAddingObjectsFromArray:array];
        }
        
        NSLog(@"%@",array);
        self.view.backgroundColor = [UIColor redColor];
        NSLog(@"%d",[NSThread isMainThread]);
    }
}
- (void)test
{
    @autoreleasepool {
        NSArray *array = @[@1,@2,@3,@4,@5];
        
        for (int i = 0; i <18; i++) {
            array = [array arrayByAddingObjectsFromArray:array];
        }
        
        NSLog(@"%@",array);
        self.view.backgroundColor = [UIColor blackColor];
        NSLog(@"%d",[NSThread isMainThread]);
        NSLog(@"这是另外一个线程的方法");
    }
}


GCD (Grand Central Dispatch) 中央调度

1.主队列

2.全局队列

3.自定义队列{串行队列,并行队列}

GCD简介

Grand Central Dispatch 简称(GCD)是苹果公司开发的技术。

以优化应用程序支持多核心处理器和其他的对称多处理系统的系统。

GCD属于函数级的多线程,性能更高,功能也更加强大。

它首次发布在OS X 10.6 iOS 4及以上也可用

GCD核心概念

任务:具有一定功能的代码段。一般是一个Block或者函数。

分发队列:GCD以队列的方式进行工作,FIFO(First In First Out)

GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务。

1.主队列

//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//向主队列中添加任务 串行队列
dispatch_async(mainQueue, ^{
NSLog(@"第一个任务...");
});
dispatch_async(mainQueue, ^{
NSLog(@"第二个任务...");
});
dispatch_async(mainQueue, ^{
NSLog(@"第三个任务...");
});
延时执行

//设置延时执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"这是五年后的你");
});

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
//NSEC_PER_MSEC 毫秒
//NSEC_PER_USEC 微秒
dispatch_after(time, mainQueue, ^{
NSLog(@"这是十年后的你");
});


不要再主线程中加入延迟,会造成阻塞。

串行与并行没有关系

主队列与全局队列都是单例,主队列串行,全局队列并行,都是直接获取,直接使用,不需要创建,也就是一直存在。

2.全局队列

//获取全局队列
dispatch_queue_t globelQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//向全局队列中添加任务,串行队列
dispatch_async(globelQueue, ^{
NSLog(@"我");
});
dispatch_async(globelQueue, ^{
NSLog(@"爱");
});
dispatch_async(globelQueue, ^{
NSLog(@"你");
});


重复执行

dispatch_apply(3, globelQueue, ^(size_t t) {
NSLog(@"重要的事情说三遍");
NSLog(@"%lu",t);
});
分组任务

//分组任务
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, globelQueue, ^{
NSLog(@"第一分组第一小队");
});
dispatch_group_async(group, globelQueue, ^{
NSLog(@"第一分组第二小队");
});
dispatch_group_async(group, globelQueue, ^{
NSLog(@"第一分组第三小队");
});
//与添加在队列的位置有关系,当处于队列末尾的时候,最后执行。
dispatch_group_notify(group, globelQueue, ^{
NSLog(@"你们先来。我不着急。");
});


3.自定义队列

自定义串行队列

//自定义串行队列
//第一个参数:队列标识符,可以获取到某一队列的标识符。
//第二个参数:队列的类型(串行或者并行)
dispatch_queue_t serialQueue = dispatch_queue_create("sss", DISPATCH_QUEUE_SERIAL);
//添加task
dispatch_async(serialQueue, ^{
NSLog(@"王");
});
dispatch_async(serialQueue, ^{
NSLog(@"颜");
});
dispatch_async(serialQueue, ^{
NSLog(@"华");
});
自定义并行队列

//自定义并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("ccc", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSLog(@"111");
});
//将任务添加到队列中,Block不执行完,下面代码不会执行。
dispatch_sync(concurrentQueue, ^{
int sum = 0;
for (int i= 0 ; i<635500000; i++) {
sum += i;
}
NSLog(@"都等着我");
});

//将此函数添加到队列的任务中,需要等其执行完成后才可以执行其他队列的任务。
//前提:自定义并行队列。
dispatch_barrier_async(concurrentQueue, ^{
int sum = 0;
for (int i= 0 ; i<635500000; i++) {
sum += i;
}
NSLog(@"让我先来");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"222");
});


//GCD 可以使用此函数,向队列中添加函数,让函数执行。
//函数类型 void(*)(void *)
dispatch_async_f(concurrentQueue, "string", function);


 //dispatch_once 添加的任务 在整个程序运行期间只会执行一次(重复添加无效)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"哈哈哈哈");
});
dispatch_once(&onceToken, ^{
NSLog(@"哈哈哈哈");
});
dispatch_once(&onceToken, ^{
NSLog(@"哈哈哈哈");
});
上述方法 可以用在单例中

伪单例方法

+ (instancetype)shareSingleton

{
static Singleton *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [self new];
});
return singleton;
}


线程间通信

分为两种:

主线程进入子线程

子线程回到主线程

方法:

dispatch_get_main_queue() GCD

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait NSObject

线程互斥

线程互斥场景

线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。

互斥无法限制访问者对资源的访问顺序,即访问是无序的,因此需要加上互斥锁来进行顺序访问,最具有代表性的就是买票系统!

NSLock类能协助完成互斥操作。

总结

NSThread、NSOperationQueue、NSObject、GCD都能实现多线程。

GCD是苹果提供的性能更高的方式。

线程尽管提升了性能,但是存在一些访问限制,比如线程同步,线程互斥等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: