Большое центральное диспетчерское вложение

У меня есть фрагмент кода, написанный внутри блока async отправки. Как показано ниже:

dispatch_queue_t queue= dispatch_queue_create("action1", NULL);

dispatch_async(queue, ^{           
   [self method1];            
   [self method2];        
   [self method3];

   dispatch_async(dispatch_get_main_queue(), ^{
       //Update UI
   });       
});

Что, если я хочу выполнить method3 в основном потоке? просто используйте

- (void) method3
{
    dispatch_async(dispatch_get_main_queue(), ^{
        //Do method 3 on main thread
    });
}

Это правильный способ сделать это ?? Я просто хочу, чтобы сначала выполнялись методы 1, 2 и 3, прежде чем мой пользовательский интерфейс будет обновлен, а метод 3 должен быть выполнен в основном потоке.


person nr5    schedule 03.02.2015    source источник


Ответы (2)


Да, это нормально. По общему признанию, кажется излишним, чтобы метод 3 отправлял что-то в основную очередь, только чтобы подпрограмма, которая вызвала этот третий метод, затем развернулась и отправила что-то еще в основную очередь. Это могло бы быть хорошо (я могу построить сценарии, в которых было бы логично именно это сделать). Имеет ли смысл отправлять дважды в основной поток полностью зависит от того, что выполняют эти два блока, но без небольшого контекста это кажется немного избыточным.

Но если отложить это в сторону, нет ничего плохого в коде, который создает очередь, отправляет ей код, но затем отправляет последний бит кода обратно в основной поток. Это очень распространенный образец.

person Rob    schedule 03.02.2015
comment
То, что я сделал, - это смещенный метод3 (на самом деле это метод SDK Google Map, который должен вызываться в основном потоке; в противном случае происходит сбой). в блоке обновления UI. Теперь все в порядке. И поскольку method3 не является ресурсоемким или трудоемким, я думаю, что переключить его на фоновый поток - это нормально. - person nr5; 03.02.2015

Следует учесть несколько моментов. Во-первых, возможно, вам следует предпринять усилия, чтобы не перенаправлять из основного потока в основной поток, чтобы ваш -method3, возможно, выглядел так:

- (void)method3
{
    dispatch_block_t work = ^{
        // The actual work that method3 does
    };

    if ([NSThread isMainThread])
        work();
    else
        dispatch_async(dispatch_get_main_queue(), work);
}

У меня есть пара "переходных" функций, которые я иногда использую для этого. Они здесь:

// If we're already on a background thread, just do it now, otherwise dispatch_async
void ensure_bg_thread_trysync(dispatch_block_t block)
{
    if (![NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
    }
}

// If we're already on the main thread, just do it now, otherwise dispatch_async
void ensure_main_thread_trysync(dispatch_block_t block)
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

Что касается объединения ваших операций друг за другом, есть несколько вариантов. Стиль прохождения продолжения немного неуклюжий в Objective-C, но его можно разумно использовать. Идея здесь в том, что ваши методы возвращают void и принимают параметр блока «что делать дальше». Итак, ваш пример с -method3, адаптированным для использования CPS, может выглядеть так (с использованием вышеуказанных функций для удобства:

- (void)method3AndThen: (dispatch_block_t)continuation
{
    ensure_main_thread_trysync(^{
        // The actual work that method3 does
        // ...

        // Then call the next thing...
        if (continuation) continuation();
    };
}


dispatch_async(queue, ^{
    [self method1];
    [self method2];
    [self method3AndThen: ^{
        ensure_main_thread_trysync(^{
            // Update UI
        });
    }];
});

Другой вариант, более целеустремленный, заключался бы в использовании NSOperationQueue и NSOperation, которые имеют встроенную поддержку цепочки зависимостей между очередями. Ваш пример может выглядеть так, используя NSOperations:

NSOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{ [self method1]; }];
NSOperation* op2 = [NSBlockOperation blockOperationWithBlock:^{ [self method2]; }];
NSOperation* op3 = [NSBlockOperation blockOperationWithBlock:^{ [self method3]; }];
NSOperation* updateUIOp = [NSBlockOperation blockOperationWithBlock:^{
    NSParameterAssert([NSThread isMainThread]);
    NSLog(@"Updating UI on Main Thread");
}];

[updateUIOp addDependency: op1];
[updateUIOp addDependency: op2];
[updateUIOp addDependency: op3];

NSOperationQueue* bgQueue = [NSOperationQueue new];
[bgQueue addOperation: op1]; // Background Thread
[bgQueue addOperation: op2]; // Background Thread

[[NSOperationQueue mainQueue] addOperation: op3]; // Main thread
[[NSOperationQueue mainQueue] addOperation: updateUIOp]; // Main thread

Примечание. Этот код предполагает, что -method1, -method2 и -method3 могут выполняться одновременно друг с другом. Если они должны выполняться последовательно, вы просто добавляете зависимости между ними, например: [op2 addDependency: op1]; [op3 addDependency: op2];

GCD - это новая популярность, и поэтому в наши дни ему, как правило, уделяется больше эфирного времени, но _15 _ / _ 16_ довольно мощный (не говоря уже о том, что они реализованы поверх GCD)

person ipmcc    schedule 03.02.2015