多线程

来源:互联网 发布:软件版本更新 编辑:程序博客网 时间:2024/06/16 08:55
多线程概述
NSThread
NSOperationQueue
GCD
多线程管理

多线程概述
程序、进程、线程
程序:由源代码生成的可执行的应用。(例如:QQ .app)
进程:一个正在运行的程序可以看做一个进程。(例如:正在运行的QQ就是一个进程),进程拥有独立运行所需的所有资源。
线程:程序中独立运行的代码段。(例如:接收QQ消息的代码)
一个程序是由一或多个线程组成。进程只负责资源的调度和分配,线程才是程序运行的执行单元,负责代码的执行。

单线程
每个正在运行的程序(即进程),至少包含一个线程,这个线程叫做主线程
主线程在程序启动时被创建,用于执行main函数
只有一个主线程的程序,称作单线程程序
主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等)。这些代码只能顺序执行,无法并发执行

多线程
拥有多个线程的程序,称作多线程程序
iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程
可以根据需求开辟若干子线程
子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行

单线程、多线程的区别
单线程程序:只有一个线程,代码顺序执行,容易出现代码阻塞(页面假死)
多线程程序:有多个线程,线程间独立执行,能够有效地避免代码阻塞,并且提高程序的运行性能
注意:iOS中关于UI的添加和刷新必须在主线程中操作

iOS系统下的多线程
iOS多线程实现种类
NSThread
NSThread是一个轻量级的多线程,它有以下两种创建方法
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
start 开启子线程
cancel 取消当前子线程

// 开启一个子线程,让for循环在子线程中去做

     //1.手动启动

    NSThread *thread = [[NSThread allocinitWithTarget:selfselector:@selector(cycle:) object:@"线程"];

    // 启动线程

    [thread start];

    [thread release];

    

    // 2.自动启动

    [NSThread detachNewThreadSelector:@selector(cycle:) toTarget:selfwithObject:nil];


#pragma mark 计算for循环和调用加载图片方法的方法

- (void)cycle:(NSString *)str

{

    for (int i = 1; i < 1000; i++) {

        NSLog(@"%d", i);

    }

    [self downLoadImage];

}


#pragma mark 加载图片

- (void)downLoadImage

{

    // 自动释放池

    @autoreleasepool {

        NSLog(@"%@ %d", [NSThread currentThread], [NSThread isMainThread]);

        //1.组拼Url地址

        NSURL *url = [NSURLURLWithString:@"http://pic1.win4000.com/wallpaper/6/53bfb7f60d991.jpg"];

        

        // 2.创建请求对象

        NSMutableURLRequest *request = [NSMutableURLRequestrequestWithURL:url];

        

        // 3.创建链接对象,发送请求,获取数据

        NSData *data = [NSURLConnection sendSynchronousRequest:request

                                             returningResponse:nil error:nil];

        

        //4.转成UIImage类型

        UIImage *image = [UIImage imageWithData:data];

        

        // 5.显示图片(更新页面需要回到主线程)

        [self performSelectorOnMainThread:@selector(showImage:)withObject:image waitUntilDone:YES];

    }

    

 

}



注意!
在多线程方法中需要添加自动释放池!
在应用程序打开的时候,系统会自动为主线程创建一个自动释放池
我们手动创建的子线程需要我们手动添加自动释放池

NSOperation
NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类
因为它是抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation或NSBlockOperation)来执行实际任务

NSOperation子类:
NSInvocationOperation、NSBlockOperation,只是一个操作,本身无主线程、子线程之分,可在任意线程中使用。通常与NSOperationQueue结合使用
NSInvocationOperation是NSOperation的子类:封装了执行操作的target和要执行的action
NSBlockOperation是NSOperation的子类:封装了需要执行的代码块

NSOperationQueue
将NSOperation添加到NSOperationQueue中实现多线程操作
NSOperationQueue是操作队列,它用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行
其中NSOperation可以调节它在队列中优先级
当最大并发数设置为1的时候,能实现线程同步

NSObject实现异步后台执行
NSObject中存在了一个最简单的后台执行的方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

// NSOperation 两个子类实现多线程的方法

    // 1.NSInvocationOperation Target Action 设计模式

    NSInvocationOperation *invocationOperation = [[NSInvocationOperationallocinitWithTarget:self selector:@selector(test:) object:@"参数"];

    

    // [invocationOperation start];

    // [invocationOperation release];

    

    // 2.NSBlockOperation Blcok模式

    NSBlockOperation *blockOperation = [NSBlockOperationblockOperationWithBlock:^{

        NSLog(@"blockOperation");

    }];

    

    // [blockOperation start];

    

    // 3.NSOperationQueue 队列方法自动实现多线程执行任务的方法

    NSOperationQueue *queue = [[NSOperationQueue allocinit];

    // 设置同时执行任务数量的最大值

    queue.maxConcurrentOperationCount 1;

    // 添加依赖关系,让一个任务等待另一个任务的完成

    [invocationOperation addDependency:blockOperation];

    // 3.1添加任务到队列中(队列执行顺序是并行执行的并且是无序的)

    [queue addOperation:invocationOperation];

    [queue addOperation:blockOperation];

    [invocationOperation release];



GCD(Grand Central Dispatch)苹果公司开发的技术。以优化应用程序支持多核心处理器和其他的对称多处理系统的系统
GCD属于函数级的多线程,性能更高,功能也更加强大
它首次发布在Mac OS X 10.6,iOS4及以上版本可用
核心概念
具有一定功能的代码段,一般是一个Block或者函数
分发队列:GCD以队列的方式进行工作,FIFO:即First In First Out
GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务

GCD中的两种队列
dispatch queue分为以下两种
SerialQueue(一次只执行一个任务。通常用于同步访问特定的资源和数据。当你创建多个Serial queue时,虽然他们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。可以实现线程同步)、Concurrent (可以并发地执行多个任务,但是遵守FIFO)

dispatch_async() //往队列中添加任务,任务会排队执行
dispatch_after() //往队列中添加任务,任务不但会排队,还会在延迟的时间点执行
dispatch_apply() //往队列中添加任务,任务会重复执行n次
dispatch_group_() //将任务添加到队列中,并添加分组标记
dispatch_group_notify() //将任务添加到队列中,当某个分组的所有任务执行完毕后,此任务才会执行
dispatch_barrier_async() //将任务添加到队列中,此任务执行的时候,其他任务停止执行
dispatch_once() //任务添加到队列中,但任务在程序运行过程中,只执行一次
dispatch_sync() //将任务添加到队列中,block不执行完,下面代码不会执行
dispatch_async_f() //将任务添加到队列中,任务是函数非block

 

    

    

    

    // 1. 使用主队列实现任务的派发,串行。主队列分派的任务,永远在主线程中

    // 1.1 拿到主队列

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    // 1.2 添加任务

    dispatch_async(mainQueue, ^{

        NSLog(@"第一个任务:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(mainQueue, ^{

        NSLog(@"第二个任务:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(mainQueue, ^{

        NSLog(@"第三个任务:当前线程是:%@", [NSThread currentThread]);

   });

   dispatch_async(mainQueue, ^{

        NSLog(@"第四个任务:当前线程是:%@", [NSThread currentThread]);

    });

   

    // 2.自己创建队列,串行,即一次只执行一个任务

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

    dispatch_queue_t myQueue = dispatch_queue_create("com.lanou,myqueue",DISPATCH_QUEUE_SERIAL);

    // 2.1.添加任务

    dispatch_async(myQueue, ^{

        NSLog(@"第一个任务是:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(myQueue, ^{

        NSLog(@"第二个任务是:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(myQueue, ^{

        NSLog(@"第三个任务是:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(myQueue, ^{

        NSLog(@"第四个任务是:当前线程是:%@", [NSThread currentThread]);

    });

    

    // 3. 自定义队列,并发执行

    dispatch_queue_t myQueue2 =dispatch_queue_create("con.lanou.myqueue2",DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(myQueue2, ^{

        NSLog(@"第一个执行的任务是:当前线程是:%@", [NSThread currentThread]);

    });

   dispatch_async(myQueue2, ^{

        NSLog(@"第二个执行的任务是:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(myQueue2, ^{

        NSLog(@"第三个执行的任务是:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(myQueue2, ^{

        NSLog(@"第四个执行的任务是:当前线程是:%@", [NSThread currentThread]);

    });


    

//    // 4.系统并行队列

    dispatch_queue_t queue2 =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0);

   dispatch_async(queue2, ^{

        NSLog(@"第一个执行的任务:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(queue2, ^{

        NSLog(@"第二个执行的任务:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(queue2, ^{

        NSLog(@"第三个执行的任务:当前线程是:%@", [NSThread currentThread]);

    });

    dispatch_async(queue2, ^{

        NSLog(@"第四个执行的任务:当前线程是:%@", [NSThread currentThread]);

    });

    

    // 系统并行队列实例

    // 5.获取网络图片数据

    __block JYFViewController *viewVC = self;

   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0), ^{

       // 5.1获取网络图片数据

        NSURL *url = [NSURLURLWithString:@"http://pic1.win4000.com/wallpaper/6/53bfb7f60d991.jpg"];

        NSData *data = [NSData dataWithContentsOfURL:url];

       UIImage *image = [UIImage imageWithData:data];

       

        // 5.2 显示图片数据

        dispatch_async(dispatch_get_main_queue(), ^{

            [viewVC showImage:image];

        });

    });

    

    // 保证代码只被运行一次

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

        static dispatch_once_t onceToken;

       dispatch_once(&onceToken, ^{

            NSLog(@"保证代码只被运行一次");

        });

    }

    

    // 让代码推迟几秒运行

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        NSLog(@"推迟五秒");

    });

    

    // 在子线程中执行4次

    dispatch_apply(4,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0), ^(size_tt){

        

        NSLog(@"打印四次");

    });


}


#pragma mark 打印对象的方法

- (void)test:(id)obj

{

    NSLog(@"%@", obj);

}

#pragma mark 展示图片

- (void)showImage:(UIImage *)image

{

    NSLog(@"%@ %d", [NSThread currentThread], [NSThread isMainThread]);

    self.imageView.image = image;

}



线程之间的通信
分为两种:
主线程进入子线程(前面的方法都可以)
子线程回到主线程
返回主线程:
GCD:dispatch_get_main_queue()
NSObject:- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

线程互斥
线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排斥性。
互斥无法限制访问者对资源的访问顺序,即访问是无序的。因此需要加上互斥锁来进行顺序访问,最具有代表性的就是买票系统!
NSLock类能协助完成互斥操作

总结:
NSThread、NSOperationQueue、NSObject、GCD都能实现多线程
GCD是苹果提供的性能更高级的方式
线程尽管提升了性能,但是存在一些访问限制,比如线程同步、线程互斥等
0 0
原创粉丝点击