05线程

2016-09-15  本文已影响12人  笛音夏扇

5.1 GCD

    // 生成一个serial dispatch queue
    dispatch_queue_t serialQueue = dispatch_queue_create("com.demo.sai", NULL);
    // 生成一个concurrent dispatch queue
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.demo.sai", DISPATCH_QUEUE_CONCURRENT);

    // 生成的dispatch queue需要手动release,注意ARC不会释放dispatch_queue_t类型的变量
    dispatch_release(serialQueue);
    dispatch_release(concurrentQueue);

    // 使用系统已经提供的方法来create queue
    dispatch_queue_t serialQueue2 = dispatch_get_main_queue();
    dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 因此真正在代码中经常是这样写的
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"execute in main thread");
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"execute in a concurrent thread");
    });

// 变更queue的priority
dispatch_set_target_queue(concurrentQueue, serialQueue);
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"dispatch_after");
    });

<font size=3 color=red>注意这个方法可以用来做指定一个时间后执行某段代码但是这个时间参数并不准确,因为这个只是在一段时间后将block加入到queue中,但是并不意味着马上执行。</font>

PS:关于用到的时间的宏的说明

NSEC:纳秒。

USEC:微妙。

SEC:秒

PER:每

所以:

NSEC_PER_SEC,每秒有多少纳秒。

USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)

NSEC_PER_USEC,每毫秒有多少纳秒。

所以,延时1秒可以写成如下几种:

dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);

dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);

dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);

最后一个“USEC_PER_SEC * NSEC_PER_USEC”,翻译过来就是“每秒的毫秒数乘以每毫秒的纳秒数”,也就是“每秒的纳秒数”

       dispatch_queue_t groupQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();

    // 第一个处理
    dispatch_group_async(group, groupQueue, ^{
        NSLog(@"first");
    });

    // 第二个处理
    dispatch_group_async(group, groupQueue, ^{
        NSLog(@"secend");
    });

    // 完成后最终的处理
    dispatch_group_notify(group, groupQueue, ^{
        NSLog(@"done");
    });

    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    if (result == 0) {
        NSLog(@"done");
    }
    else {
        NSLog(@"not done");
    }
    // 需要release
    dispatch_release(group);

-dispatch_barrier_async,在代码中会等待之前加入某个queue的block全部执行完,然后使得concurrent queue变为一个serial queue,只能执行用dispatch_barrier_async添加的block,等完成后,queue变回并发。典型应用场景DB read的操作使用dispatch_async,当需要write DB时使用dispatch_barrier_async,可以保证数据的一致性,也相当于给写操作加了锁(好吧这样说不够严谨)。

-dispatch_sync,同步的将block放到某个queue中,执行这个函数的线程会阻塞等待block执行完成。很容易导致死锁,目前看最好别用。

关于dispatch_sync导致死锁的问题:
dispatch_sync(dispatch_get_main_queue, ...)这样写一定会死锁,dispatch_(a)sync这两个函数本质上是将block放到一个queue中,只不过一个会阻塞当前调用函数的线程,一个不会。

dispatch_async(dispatch_get_main_queue, ...)假设是main thread执行这个函数,那么线程不会等待block执行,虽然这个block是在main thread中执行的,最有可能的是在下一个loop中才会执行block。

dispatch_async(dispatch_get_global_queue(...), ...)假设是main thread执行这个函数,那么线程不会等待block执行,而是由系统分配另一个线程完成block,这个方式就是典型的多线程。

dispatch_sync(dispatch_get_main_queue, ...)假设是main thread执行这个函数,那么线程会等待block执行,但是这个block又是在main thread执行的,导致死锁。

dispatch_sync(dispatch_get_global_queue(...), ...)假设是main thread执行这个函数,那么线程会等待block执行,由系统分配另一个线程完成block,这个方式可用,但是最好不用main thread,而是自己创建一个serial queue。

  // 使用系统已经提供的方法来create queue
    dispatch_queue_t serialQueue2 = dispatch_get_main_queue();
    dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 因此真正在代码中经常是这样写的
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"execute in main thread");
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"execute in a concurrent thread");
    });

    // 变更queue的priority
    //    dispatch_set_target_queue(concurrentQueue, serialQueue);

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"dispatch_after");
    });

    dispatch_queue_t groupQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();

    // 第一个处理
    dispatch_group_async(group, groupQueue, ^{
        NSLog(@"first");
    });

    // 第二个处理
    dispatch_group_async(group, groupQueue, ^{
        NSLog(@"secend");
    });

    // 完成后最终的处理
    dispatch_group_notify(group, groupQueue, ^{
        NSLog(@"done");
    });

    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    if (result == 0) {
        NSLog(@"result done");
    }
    else {
        NSLog(@"result not done");
    }
    // 需要release
    dispatch_release(group);

    dispatch_async(concurrentQueue2, ^{
        NSLog(@"read1");
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"read2");
    });

    dispatch_barrier_async(concurrentQueue2, ^{
        NSLog(@"write");
    });

    dispatch_async(concurrentQueue2, ^{
        NSLog(@"read3");
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"read4");
    });

2016-07-09 14:46:28.500 CRHelper[3027:231293] execute in a concurrent thread
2016-07-09 14:46:28.500 CRHelper[3027:231294] first
2016-07-09 14:46:28.500 CRHelper[3027:231297] secend
2016-07-09 14:46:28.502 CRHelper[3027:231297] done
2016-07-09 14:46:28.502 CRHelper[3027:231205] result done
2016-07-09 14:46:28.507 CRHelper[3027:231297] read1
2016-07-09 14:46:28.507 CRHelper[3027:231293] read2
2016-07-09 14:46:28.507 CRHelper[3027:231390] write
2016-07-09 14:46:28.507 CRHelper[3027:231294] read3
2016-07-09 14:46:28.507 CRHelper[3027:231297] read4
2016-07-09 14:46:28.546 CRHelper[3027:231205] execute in main thread
2016-07-09 14:46:31.782 CRHelper[3027:231294] dispatch_after

"execute in main thread"这句话在代码顺序中是第一个但是却在倒数第二个输出,因为是在main thread执行的,可以很明确的看出来这个代码是在下一个loop中执行的,还有就是dispatch_after也并不是严格的按照3秒后执行的.

    NSArray* arr = [NSArray arrayWithObjects:@1, @2, nil];
    for (NSInteger i = 0; i < [arr count]; i++) {
        NSLog(@"%@", [arr objectAtIndex:i]);
    }
    dispatch_async(concurrentQueue2, ^{
        dispatch_apply([arr count], concurrentQueue2, ^(size_t i) {
            NSLog(@"%@", [arr objectAtIndex:i]);
        });
    });

5.2 NSThread和performSelector

- (void)doSome:(NSString*)arg
{
    NSLog(@"%@ -> %@", [NSThread currentThread], arg);
}

- (void)startThread
{
    NSThread* th = [[NSThread alloc] initWithTarget:self selector:@selector(doSome:) object:@"1"];
    BOOL state = [th isMainThread];
    state = [th isCancelled];
    state = [th isFinished];
    state = [th isExecuting];

    [th start];

    [self performSelectorOnMainThread:@selector(doSome:) withObject:@"2" waitUntilDone:NO];
    [self performSelector:@selector(doSome:) withObject:@"3" afterDelay:3];

    [self performSelector:@selector(doSome:) withObject:@"4"];
}
2016-08-28 22:40:45.055 ARCTest[3619:236163] <NSThread: 0x7fc0c3501b70>{number = 1, name = main} -> 4
2016-08-28 22:40:45.055 ARCTest[3619:236310] <NSThread: 0x7fc0c3723f30>{number = 2, name = (null)} -> 1
2016-08-28 22:40:45.077 ARCTest[3619:236163] <NSThread: 0x7fc0c3501b70>{number = 1, name = main} -> 2
2016-08-28 22:40:48.056 ARCTest[3619:236163] <NSThread: 0x7fc0c3501b70>{number = 1, name = main} -> 3

5.3 NSOperation

5.4 锁

NSLock控制麻烦,而且要考虑死锁。

@synchronized(self)如果很多方法都用self做锁,那么会导致一个长时间执行的方法阻塞其它方法,所以如果用最好不要都使用一个对象来作为锁对象。

GCD的锁不需要关注实现,而且是深层次实现的,比较高效,并且方法众多,可以时间多种锁的需要。

<span id="choose">5.5 多线程编程的选择</span>

1)GCD是纯C语言的API,而操作队列则是Object-C的对象。

2)在GCD中,任务用块(block)来表示,而块是个轻量级的数据结构;相反操作队列中的『操作』NSOperation则是个更加重量级的Object-C对象。

3)具体该使用GCD还是使用NSOperation需要看具体的情况

4)需要注意,如果直接使用NSOperation的start方法是直接在调用线程执行的,这意味着可能是在UI线程执行。

1)NSOperationQueue可以方便的调用cancel方法来取消某个操作,而GCD中的任务是无法被取消的(安排好任务之后就不管了)。

2)NSOperation可以方便的指定操作间的依赖关系。

3)NSOperation可以通过KVO提供对NSOperation对象的精细控制(如监听当前操作是否被取消或是否已经完成等)

4)NSOperation可以方便的指定操作优先级。操作优先级表示此操作与队列中其它操作之间的优先关系,优先级高的操作先执行,优先级低的后执行。

5)通过自定义NSOperation的子类可以实现操作重用,

5.6 并发

上一篇下一篇

猜你喜欢

热点阅读