ios dispatch_async(dispatch_get_main_queue(), ^{...}); 等到完成?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17490286/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:14:51  来源:igfitidea点击:

Does dispatch_async(dispatch_get_main_queue(), ^{...}); wait until done?

iosobjective-cmultithreadinggrand-central-dispatchdispatch-async

提问by EmptyStack

I have a scenario in my app, where I want to do some time consuming task which consists of some data processing as well as UI update, in a method. My method looks like this,

我的应用程序中有一个场景,我想在一个方法中执行一些耗时的任务,其中包括一些数据处理和 UI 更新。我的方法看起来像这样,

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1
    // UI UPDATE 1

    // DATA PROCESSING 2
    // UI UPDATE 2

    // DATA PROCESSING 3
    // UI UPDATE 3
} 

As it is time consuming I wanted to do the data processing on the background thread, using,

由于它很耗时,我想在后台线程上进行数据处理,使用,

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

But as both data processing and UI updates are in the same method, I wanted to move only the UI updates in main thread using,

但是由于数据处理和 UI 更新都采用相同的方法,我只想使用以下方法移动主线程中的 UI 更新,

dispatch_async(dispatch_get_main_queue(), ^{

Finally my method looks like this,

最后我的方法看起来像这样,

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1 
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 1
    });

    /* I expect the control to come here after UI UPDATE 1 */

    // DATA PROCESSING 2
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 2
    });

    /* I expect the control to come here after UI UPDATE 2 */

    // DATA PROCESSING 3
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 3
    });
}

Does this really work? Is this really a good practice? What is the best way to achieve this?

这真的有效吗?这真的是一个好习惯吗?实现这一目标的最佳方法是什么?

P.S. All these three operations are interrelated to each other.

PS 所有这三个操作都是相互关联的。



EDIT:Sorry guys. I have missed a line in the above code. My actual code looks like this.

编辑:对不起,伙计们。我错过了上面代码中的一行。我的实际代码是这样的。

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // DATA PROCESSING 1 
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 1
        });

        /* I expect the control to come here after UI UPDATE 1 */

        // DATA PROCESSING 2
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 2
        });

        /* I expect the control to come here after UI UPDATE 2 */

        // DATA PROCESSING 3
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 3
        });
    });
}

Once again, I really apologize for the confusion.

再一次,我真的为造成的混乱道歉。

回答by David R?nnqvist

No it doesn't wait and the way you are doing it in that sample is not good practice.

不,它不会等待,并且您在该示例中的操作方式不是好的做法。

dispatch_asyncis always asynchronous. It's just that you are enqueueing all the UI blocks to the same queue so the different blocks will run in sequence but parallel with your data processing code.

dispatch_async总是异步的。只是您将所有 UI 块排队到同一个队列中,这样不同的块将按顺序运行,但与您的数据处理代码并行。

If you want the update to wait you can use dispatch_syncinstead.

如果您希望更新等待,您可以dispatch_sync改用。

// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
});

Another approach would be to nest enqueueing the block. I wouldn'trecommend it for multiple levels though.

另一种方法是嵌套排队块。不过,我不会推荐它用于多个级别。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Background work

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

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // Background work

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

If you need the UI updated to wait then you should use the synchronous versions. It's quite okay to have a background thread wait for the main thread. UI updates should be very quick.

如果您需要更新 UI 以等待,那么您应该使用同步版本。让后台线程等待主线程是完全可以的。UI 更新应该非常快。

回答by StatusReport

You have to put your main queue dispatching in the block that runs the computation. For example (here I create a dispatch queue and don't use a global one):

您必须将主队列调度放在运行计算的块中。例如(这里我创建了一个调度队列并且不使用全局队列):

dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
  // Do some computation here.

  // Update UI after computation.
  dispatch_async(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
  });
});

Of course, if you create a queue don't forget to dispatch_releaseif you're targeting an iOS version before 6.0.

当然,如果你创建了一个队列,不要忘记dispatch_release你的目标是 6.0 之前的 iOS 版本。

回答by Rob

Your proposed doCalculationsAndUpdateUIsdoes data processing and dispatches UI updates to the main queue. I presume that you have dispatched doCalculationsAndUpdateUIsto a background queue when you first called it.

您建议进行doCalculationsAndUpdateUIs数据处理并将 UI 更新分派到主队列。我假设您doCalculationsAndUpdateUIs在第一次调用它时已经分派到后台队列。

While technically fine, that's a little fragile, contingent upon your remembering to dispatch it to the background every time you call it: I would, instead, suggest that you do your dispatch to the background and dispatch back to the main queue from within the same method, as it makes the logic unambiguous and more robust, etc.

虽然技术上很好,但这有点脆弱,这取决于您每次调用它时都记得将它分派到后台:相反,我建议您将其分派到后台并从同一个队列中分派回主队列方法,因为它使逻辑明确且更健壮等。

Thus it might look like:

因此它可能看起来像:

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

        // DATA PROCESSING 1 

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 1
        });

        /* I expect the control to come here after UI UPDATION 1 */

        // DATA PROCESSING 2

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 2
        });

        /* I expect the control to come here after UI UPDATION 2 */

        // DATA PROCESSING 3

        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATION 3
        });
    });
}

In terms of whether you dispatch your UI updates asynchronously with dispatch_async(where the background process will notwait for the UI update) or synchronously with dispatch_sync(where it willwait for the UI update), the question is whywould you want to do it synchronously: Do you really want to slow down the background process as it waits for the UI update, or would you like the background process to carry on while the UI update takes place.

在是否派遣你的UI与异步更新项dispatch_async(其中后台进程将不会与等待UI更新)或同步dispatch_sync(它等待UI更新),问题是为什么会要同步做到这一点:您真的想在后台进程等待 UI 更新时减慢它的速度,还是希望后台进程在 UI 更新发生时继续进行。

Generally you would dispatch the UI update asynchronously with dispatch_asyncas you've used in your original question. Yes, there certainly are special circumstances where you need to dispatch code synchronously (e.g. you're synchronizing the updates to some class property by performing all updates to it on the main queue), but more often than not, you just dispatch the UI update asynchronously and carry on. Dispatching code synchronously can cause problems (e.g. deadlocks) if done sloppily, so my general counsel is that you should probably only dispatch UI updates synchronously if there is some compelling need to do so, otherwise you should design your solution so you can dispatch them asynchronously.

通常,您会dispatch_async像在原始问题中使用的那样异步调度 UI 更新。是的,在某些特殊情况下,您需要同步调度代码(例如,您通过在主队列上执行所有更新来将更新同步到某个类属性),但通常情况下,您只是调度 UI 更新异步并继续。如果草率地执行同步分派代码可能会导致问题(例如死锁),所以我的一般建议是,如果有一些令人信服的需要,您可能应该只同步分派 UI 更新,否则您应该设计您的解决方案,以便您可以异步分派它们.



In answer to your question as to whether this is the "best way to achieve this", it's hard for us to say without knowing more about the business problem being solved. For example, if you might be calling this doCalculationsAndUpdateUIsmultiple times, I might be inclined to use my own serial queue rather than a concurrent global queue, in order to ensure that these don't step over each other. Or if you might need the ability to cancel this doCalculationsAndUpdateUIswhen the user dismisses the scene or calls the method again, then I might be inclined to use a operation queue which offers cancelation capabilities. It depends entirely upon what you're trying to achieve.

在回答您关于这是否是“实现这一目标的最佳方式”的问题时,我们很难在不了解更多正在解决的业务问题的情况下说出来。例如,如果您可能doCalculationsAndUpdateUIs多次调用它,我可能倾向于使用我自己的串行队列而不是并发全局队列,以确保它们不会相互交叉。或者,如果您可能需要在doCalculationsAndUpdateUIs用户关闭场景或再次调用该方法时取消此功能,那么我可能倾向于使用提供取消功能的操作队列。这完全取决于您要实现的目标。

But, in general, the pattern of asynchronously dispatching a complicated task to a background queue and then asynchronously dispatching the UI update back to the main queue is very common.

但是,一般来说,将复杂任务异步分派到后台队列,然后将 UI 更新异步分派回主队列的模式非常常见。

回答by Wain

No, it won't wait.

不,它不会等待。

You could use performSelectorOnMainThread:withObject:waitUntilDone:.

你可以使用performSelectorOnMainThread:withObject:waitUntilDone:.

回答by Ashokios

If you want to run a single independent queued operation and you're not concerned with other concurrent operations, you can use the global concurrent queue:

如果你想运行一个独立的队列操作并且你不关心其他并发操作,你可以使用全局并发队列:

dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

This will return a concurrent queue with the given priority as outlined in the documentation:

这将返回一个具有给定优先级的并发队列,如文档中所述:

DISPATCH_QUEUE_PRIORITY_HIGH Items dispatched to the queue will run at high priority, i.e. the queue will be scheduled for execution before any default priority or low priority queue.

DISPATCH_QUEUE_PRIORITY_DEFAULT Items dispatched to the queue will run at the default priority, i.e. the queue will be scheduled for execution after all high priority queues have been scheduled, but before any low priority queues have been scheduled.

DISPATCH_QUEUE_PRIORITY_LOW Items dispatched to the queue will run at low priority, i.e. the queue will be scheduled for execution after all default priority and high priority queues have been scheduled.

DISPATCH_QUEUE_PRIORITY_BACKGROUND Items dispatched to the queue will run at background priority, i.e. the queue will be scheduled for execution after all higher priority queues have been scheduled and the system will run items on this queue on a thread with background status as per setpriority(2) (i.e. disk I/O is throttled and the thread's scheduling priority is set to lowest value).

DISPATCH_QUEUE_PRIORITY_HIGH 分派到队列的项目将以高优先级运行,即队列将在任何默认优先级或低优先级队列之前被调度执行。

DISPATCH_QUEUE_PRIORITY_DEFAULT 分派到队列的项目将以默认优先级运行,即队列将在所有高优先级队列都被调度之后但在任何低优先级队列被调度之前被调度执行。

DISPATCH_QUEUE_PRIORITY_LOW 分派到队列的项目将以低优先级运行,即在所有默认优先级和高优先级队列都被调度后,队列将被调度执行。

DISPATCH_QUEUE_PRIORITY_BACKGROUND 分配到队列的项目将以后台优先级运行,即队列将在所有更高优先级的队列都被调度后被调度执行,并且系统将根据 setpriority(2) 在具有后台状态的线程上运行此队列上的项目(即磁盘 I/O 被限制并且线程的调度优先级被设置为最低值)。

回答by Moin Shirazi

dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
  // Do some computation here.

  // Update UI after computation.
  dispatch_async(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
  });
});

回答by ChavirA

The good practice is: Dispatch Groups

好的做法是:调度组

dispatch_group_t imageGroup = dispatch_group_create();

dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
    // Image successfully uploaded to S3
    dispatch_group_leave(imageGroup);
}];

dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
    // Image url updated
    dispatch_group_leave(imageGroup);
}];

dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
    // We get here when both tasks are completed
});

回答by kokemomuke

OK, there are two ways of doing that:

好的,有两种方法可以做到这一点:

// GLOBAL_CONCURRENT_QUEUE


- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE 
{
    dispatch_queue_t globalConcurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(globalConcurrentQ, ^{

       // DATA PROCESSING 1
       sleep(1);
       NSLog(@"Hello world chekpoint 1");
       dispatch_sync(dispatch_get_main_queue(), ^{
           // UI UPDATION 1
           sleep(1);
           NSLog(@"Hello world chekpoint 2");
       });

        /* the control to come here after UI UPDATION 1 */
        sleep(1);
        NSLog(@"Hello world chekpoint 3");
        // DATA PROCESSING 2

        dispatch_sync(dispatch_get_main_queue(), ^{
            // UI UPDATION 2
            sleep(1);
            NSLog(@"Hello world chekpoint 4");
        });

        /* the control to come here after UI UPDATION 2 */
        sleep(1);
        NSLog(@"Hello world chekpoint 5");
        // DATA PROCESSING 3

        dispatch_sync(dispatch_get_main_queue(), ^{
            // UI UPDATION 3
            sleep(1);
            NSLog(@"Hello world chekpoint 6");
        });
   });
}



// SERIAL QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE 
{

    dispatch_queue_t serialQ = dispatch_queue_create("com.example.MyQueue", NULL);
    dispatch_async(serialQ, ^{

       // DATA PROCESSING 1
       sleep(1);
       NSLog(@"Hello world chekpoint 1");

       dispatch_sync(dispatch_get_main_queue(), ^{
           // UI UPDATION 1
           sleep(1);
           NSLog(@"Hello world chekpoint 2");
       });


       sleep(1);
       NSLog(@"Hello world chekpoint 3");
       // DATA PROCESSING 2

       dispatch_sync(dispatch_get_main_queue(), ^{
           // UI UPDATION 2
           sleep(1);
           NSLog(@"Hello world chekpoint 4");
       });  
   });
}