iOS 多线程相关之performSelector、死锁

  1. performSelector
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //在当前线程延迟1s执行,响应了OC语言的动态性:延迟到运行时才绑定方法
    [self performSelector:@selector(aaa) withObject:nil afterDelay:1];
    // 回到主线程,waitUntilDone:是否将该回调方法执行完再执行后面的代码
    // 如果为YES:就必须等回调方法执行完成之后才能执行后面的代码,说白了就是阻塞当前的线程
    // 如果是NO:就是不等回调方法结束,不会阻塞当前线程
    [self performSelectorOnMainThread:@selector(aaa) withObject:nil waitUntilDone:YES];
    // 开辟子线程
    [self performSelectorInBackground:@selector(aaa) withObject:nil];
    //在指定线程执行
    [self performSelector:@selector(aaa) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
  • 需要注意的是:如果是带afterDelay的延时函数,会在内部创建一个NSTimer,然后添加到当前线程的Runloop中。也就是如果当前线程没有开启runloop,该方法会失效。在子线程中,需要启动runloop(注意调用顺序)

    1
    2
    [self performSelector:@selector(aaa) withObject:nil afterDelay:1];
    [[NSRunLoop currentRunLoop] run];
  • performSelector:withObject:只是一个单纯的消息发送,和时间没有一点关系。所以不需要添加到子线程的Runloop中也能执行

  • 下面代码片段的test方法会去执行吗?
    1
    2
    3
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self performSelector:@selector(test:) withObject:nil afterDelay:0];
    });

这里的test方法是不会去执行的,原因在于- (void)performSelector: withObject: afterDelay:这个方法要创建提交任务到runloop上的,而gcd底层创建的线程是默认没有开启对应runloop的,所有这个方法就会失效。
而如果将dispatch_get_global_queue改成主队列,由于主队列所在的主线程是默认开启了runloop的,就会去执行(将dispatch_async改成同步,因为同步是在当前线程执行,那么如果当前线程是主线程,test方法也是会去执行的)

  1. 死锁
  • 死锁就是队列引起的循环等待,一个比较常见的死锁例子:主队列同步

    1
    2
    3
    4
    5
    6
    - (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"deallock");
    });
    }
  • 在主线程中运用主队列同步,也就是把任务放到了主线程的队列中。同步对于任务是立刻执行的,那么当把任务放进主队列时,它就会立马执行,只有执行完这个任务,viewDidLoad才会继续向下执行。而viewDidLoad和任务都是在主队列上的,由于队列的先进先出原则,任务又需等待viewDidLoad执行完毕后才能继续执行,viewDidLoad和这个任务就形成了相互循环等待,就造成了死锁。
    想避免这种死锁,可以将同步改成异步dispatch_async或者将dispatch_get_main_queue换成其他串行或并行队列,都可以解决

  • 同样,下边的代码也会造成死锁:

    1
    2
    3
    4
    5
    6
    dispatch_queue_t serialQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{
    dispatch_sync(serialQueue, ^{
    NSLog(@"deadlock");
    });
    });
  • 外面的函数无论是同步还是异步都会造成死锁。这是因为里面的任务和外面的任务都在同一个serialQueue队列内,又是同步,这就和上边主队列同步的例子一样造成了死锁。
    解决方法也和上边一样,将里面的同步改成异步dispatch_async或者将serialQueue换成其他串行或并行队列,都可以解决

-------------本文结束感谢您的阅读-------------
最近的文章

iOS RunLoop

RunLoop概念 RunLoop是通过内部维护的事件循环(Event Loop)来对事件/消息进行管理的一个对象 没有消息处理时,休眠以避免资源占用;有消息需要处理时,立刻被唤醒 为什么main函数不会退出12345int main(int argc, char * argv[]) &#123 …

代码库 继续阅读
更早的文章

iOS 中事件的响应链和传递链

iOS事件链有两条:事件的响应链;Hit-Testing事件的传递链 响应链:由离用户最近的view向系统传递。initial view –> super view –> ….. –> view controller –> window –> Application …

代码库 继续阅读