Vickate_iOS_浅说NSOperationQueue、GCD、NSThread

来源:互联网 发布:dikies是什么档次知乎 编辑:程序博客网 时间:2024/05/20 22:29

由于最近在做关于 IM 文件下载的需求,想到了队列,把线程的相关知识都看了遍,希望把自己理解的东西能分享出去^_^。

常用的线程有 NSThreadNSOperationQueueGCDpthread

这里写图片描述

1、NSThread

优点:NSThread 比其他两个轻量级

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

NSThread的使用:

创建方式:

[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];[myThread start];

示例代码:

#pragma mark ***<下面以下载图片为例>***- (void)viewDidLoad{    [super viewDidLoad];    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL]; // 创建线程下载图片    [thread start];}-(void)downloadImage:(NSString *) url{    NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];    UIImage *image = [[UIImage alloc]initWithData:data];    if(image == nil){    }else{        [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES]; // 主线程:下载完成,展示下载的图片    }}-(void)updateUI:(UIImage*) image{    self.imageView.image = image;}

需要用到 线程锁 的同学可以参考链接:http://download.csdn.net/detail/totogo2010/4591149

2、NSOperationQueue 与 NSOperation

常用于多任务下载、断点下载

(1)NSOperation是对GCD中的block进行的封装,它也表示一个要被执行的任务。和GCD的block类似,NSOperation对象有一个start()方法表示开始执行这个任务。

(2)不仅如此,NSOperation表示的任务还可以被取消。它还有三种状态isExecuted、isFinished和isCancelled以方便我们铜鼓KVC对它的状态进行监听。

- (BOOL)isReady {    self.state = ConcurrentOperationReadyState;    return self.state == ConcurrentOperationReadyState;}- (BOOL)isExecuting{    return self.state == ConcurrentOperationExecutingState;}- (BOOL)isFinished{    return self.state == ConcurrentOperationFinishedState;}- (void)start {    __weak typeof(self) weakSelf = self;    dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);    dispatch_after(time, dispatch_get_main_queue(), ^{        //kvo:结束        [weakSelf willChangeValueForKey:@"isFinished"];        weakSelf.state = ConcurrentOperationFinishedState;        [weakSelf didChangeValueForKey:@"isFinished"];        NSLog(@"finished :%@",weakSelf);    });    NSLog(@"start called");    //kvo:正在执行    [weakSelf willChangeValueForKey:@"isExecuting"];    weakSelf.state = ConcurrentOperationExecutingState;    [weakSelf didChangeValueForKey:@"isExecuting"];}

(3)队列的两种方式:主队列和非主队列。

(4)NSOperationQueue有一个属性叫maxConcurrentOperationCount,它表示最多支持多少个NSOperation并发执行。如果maxConCurrentOperationCount被设置为1,就以为这个队列是串行队列。

//设置并发数目为2queue.maxConcurrentOperationCount = 2;

(5)队列的优先级

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {    NSOperationQueuePriorityVeryLow = -8L,    NSOperationQueuePriorityLow = -4L,    NSOperationQueuePriorityNormal = 0,    NSOperationQueuePriorityHigh = 4,    NSOperationQueuePriorityVeryHigh = 8};/*当operation被添加到队列之后,NSOperationQueue会浏览所有的operation,优先运行那些处于ready状态且优先级较高的操作。*///设置优先级[operation1 setQueuePriority:NSOperationQueuePriorityVeryLow];

(6)添加依赖:NSOperation的强大之处

/* 当operation1依赖于operation2的时候,系统可以保证只有当operation2结束的时候,operation1才会运行,而且依赖不局限于一个队列,你可以依赖一个不同队列的NSOperation。*/NSOperationQueue  *queue1 = [NSOperationQueue new];NSOperationQueue  *queue2 = [NSOperationQueue new];NonConcurrentOperation *op1 = [[NonConcurrentOperation alloc] initWithNumber:@(1)];NonConcurrentOperation *op2 = [[NonConcurrentOperation alloc] initWithNumber:@(2)];NonConcurrentOperation *op3 = [[NonConcurrentOperation alloc] initWithNumber:@(3)];NonConcurrentOperation *op4 = [[NonConcurrentOperation alloc] initWithNumber:@(4)];//添加依赖[op1 addDependency:op2];[op2 addDependency:op3];//可以依赖不同队列的operation[op3 addDependency:op4];//添加到不同队列中[queue1 addOperation:op1];[queue1 addOperation:op2];[queue1 addOperation:op3];[queue2 addOperation:op4]; 

输出结果:

NSOperations[2105:179596] main called, 4NSOperations[2105:179596] main called, 3NSOperations[2105:179596] main called, 2NSOperations[2105:179596] main called, 1

(7)个人最爱用的创建方式:

NSOperationQueue *opQ = [NSOperationQueue new];[opQ addOperationWithBlock:^{     // 开始任务     [self.downloadTask resume];}];

3、GCD

(1)三种队列方式:

1>串行队列(关键字:DISPATCH_QUEUE_SERIAL)

2>并发队列(关键字:DISPATCH_QUEUE_CONCURRENT)

3>主队列(dispatch_get_main_queue(),这是一个特殊的串行队列,而且队列中的任务一定会在主线程中执行)

(2)执行方式:

1>同步执行(关键字:dispatch_sync)

2>异步执行(关键字:dispatch_async)

(3)GCD任务组

//创建一个并行队列dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);//创建一个组dispatch_group_t group = dispatch_group_create();//添加任务dispatch_group_async(group, concurrentQueue, ^{      dispatch_async(dispatch_get_main_queue(), ^{             NSLog(@"任务一完成");      });});dispatch_group_async(group, concurrentQueue, ^{      dispatch_async(dispatch_get_main_queue(), ^{              NSLog(@"任务二完成");      });});dispatch_group_async(group, concurrentQueue, ^{      dispatch_async(dispatch_get_main_queue(), ^{              NSLog(@"任务三完成");      });});//监听group里面的任务什么时候全完成dispatch_group_notify(group, dispatch_get_main_queue(), ^{             NSLog(@"group里面的所有任务已完成");});//releasedispatch_release(concurrentQueue);dispatch_release(group);

(4)dispatch_group_wait

dispatch_group_wait(dispatch_group_t  _Nonnull group, dispatch_time_t timeout)// 1.第一个参数表示需要要等待的 group,第二个参数则表示等待时间。返回值表示经过指定的等待时间之后属于这个 group 的任务是否已经全部执行完,如果是则返回 0,否则返回非 0。// 2.第二个 dispatch_time_t 类型的参数还有两个特殊值:// DISPATCH_TIME_NOW :表示立刻检查属于这个 group 的任务是否已经完成// DISPATCH_TIME_FOREVER:表示一直等到属于这个 group 的任务全部完成

(5)dispatch_after

dispatch_after(dispatch_time_t when, dispatch_queue_t  _Nonnull queue, ^{     ......})/*  dispatch_after仅表示在指定时间后提交任务,而非执行任务。dispatch_after 方法有三个参数:1.表示时间,也就是从现在起往后多长时间;2.表示要提交的任务;3.表示要提交到哪个队列;*/

(6)dispatch_suspend和dispatch_resume

// 这些函数不会影响到队列中已经执行的任务,队列暂停后,已经添加到队列中但还没有执行的任务是不会执行的,直到队列被恢复。dispatch_suspend(queue) //暂停某个队列dispatch_resume(queue)  //恢复某个队列

(7)dispatch_once:(比较常用,在初始化单例的时候)

// dispatch_once 函数可以确保某个 block 在应用程序执行的过程中只被处理一次,而且它是线程安全的,常用于单例。static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{      code to be executed once});

4、pthread

前年年底的时候,ibireme大神的YYKit火起来了,下载了 demo 硬是没有看懂,上面就有宏看到过 pthread。

(1)先看看YY定义的宏

static inline void pthread_mutex_init_recursive(pthread_mutex_t *mutex, bool recursive) {#define YYMUTEX_ASSERT_ON_ERROR(x_) do { \__unused volatile int res = (x_); \assert(res == 0); \} while (0)    assert(mutex != NULL);    if (!recursive) {        YYMUTEX_ASSERT_ON_ERROR(pthread_mutex_init(mutex, NULL));    } else {        pthread_mutexattr_t attr;        YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_init (&attr));        YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE));        YYMUTEX_ASSERT_ON_ERROR(pthread_mutex_init (mutex, &attr));        YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_destroy (&attr));    }#undef YYMUTEX_ASSERT_ON_ERROR}

pthread_mutex_t是什么鬼?

int pthread_mutex_init(pthread_mutex_t * __restrict,const pthread_mutexattr_t * __restrict);

是用这个函数创建出来的。函数是以动态的方式创建互斥锁的,参数attr指定了新建互斥锁的属性。

recursive这个bool值为false时,attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁。

recursive这个bool值为true时,配置互斥锁属性创建相应的互斥锁。

PTHREAD_MUTEX_NORMAL:不进行deadlock detection(死锁检测)。当进行relock时,这个mutex就导致deadlock。对一个没有进行lock或者已经unlock的对象进行unlock操作,结果也是未知的。

PTHREAD_MUTEX_ERRORCHECK:和PTHREAD_MUTEX_NORMAL相比,PTHREAD_MUTEX_ERRORCHECK会进行错误检测,以上错误行为都会返回一个错误。

PTHREAD_MUTEX_RECURSIVE:和semaphore(信号量)有个类似的东西,mutex会有个锁住次数的概念,第一次锁住mutex的时候,锁住次数设置为1,每一次一个线程unlock这个mutex时,锁住次数就会减1。当锁住次数为0时,其他线程就可以获得该mutex锁了。同样,对一个没有进行lock或者已经unlock的对象进行unlock操作,将返回一个错误。

PTHREAD_MUTEX_DEFAULT:默认PTHREAD_MUTEX_NORMAL。

(2)再看看YY如何使用该宏

// 这边为了防止多线程资源抢夺的问题,先进行lock下,等数据操作完毕后释放unlock,有没有一种豁然开朗的感觉呢// 平时我们在多线程操作的时候也可以使用NSLock、synchronized来进行加锁,yy使用了更加偏向底层的pthread- (YYImageFrame *)frameAtIndex:(NSUInteger)index decodeForDisplay:(BOOL)decodeForDisplay {    YYImageFrame *result = nil;    pthread_mutex_lock(&_lock);    result = [self _frameAtIndex:index decodeForDisplay:decodeForDisplay];    pthread_mutex_unlock(&_lock);    return result;}

(3)pthread使用
1>声明函数

void *func(void *argu) {    char *m = (char *)argu;    pthread_mutex_lock(&mutex);    while (*m != '\0') {        printf("%c", *m);        fflush(stdout);        sleep(3);        m++;    }    printf("\n");    pthread_mutex_unlock(&mutex);    return 0;}

2>mutex使用

    int rc1, rc2;    char *str1 = "Hi";    char *str2 = "Boy!";    pthread_t thread1, thread2;    pthread_mutex_init(&mutex, NULL);    if ((rc1 = pthread_create(&thread1, NULL, func, str1))) {        fprintf(stdout, "thread1 creat fail : %d \n!", rc1);    }    if ((rc2 = pthread_create(&thread2, NULL, func, str2))) {        fprintf(stdout, "thread2 creat fail : %d \n!", rc2);    }    // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/pthread_join.3.html#//apple_ref/c/func/pthread_join    // 等待一个线程的结束,当函数返回时,被等待的线程资源被收回。若线程已经被收回,那么该函数会立即返回    pthread_join(thread1, NULL);    pthread_join(thread2, NULL);    printf("这边只有线程被回收后才会执行!");

参考链接:

1、YYKit源码分析—pthread:http://www.jianshu.com/p/5bf819bf60ba

2、iOS多线程及队列的使用:http://blog.csdn.net/lengshengren/article/details/17267555

0 0