Vickate_iOS_浅说NSOperationQueue、GCD、NSThread

由于最近在做关于 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,就以为这个队列是串行队列。

//设置并发数目为2
queue.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, 4
NSOperations[2105:179596] main called, 3
NSOperations[2105:179596] main called, 2
NSOperations[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里面的所有任务已完成");
});
//release
dispatch_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值