简体   繁体   English

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

[英]Does dispatch_async(dispatch_get_main_queue(), ^{…}); wait until done?

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. 我的应用程序中有一个场景,我想在一个方法中做一些耗费时间的任务,其中包括一些数据处理和UI更新。 My method looks like this, 我的方法看起来像这样,

- (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? 实现这一目标的最佳方法是什么?

PS 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. 再一次,我真的为这种混乱道歉。

No it doesn't wait and the way you are doing it in that sample is not good practice. 不,它不等待,你在那个样本中做的方式不是好习惯。

dispatch_async is always asynchronous . dispatch_async始终是异步的 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. 只是您将所有UI块排入同一队列,这样不同的块将按顺序运行,但与您的数据处理代码并行。

If you want the update to wait you can use dispatch_sync instead. 如果您希望更新等待,则可以使用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't recommend 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. 如果您需要更新UI以等待,那么您应该使用同步版本。 It's quite okay to have a background thread wait for the main thread. 有一个后台线程等待主线程是完全可以的。 UI updates should be very quick. UI更新应该非常快。

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_release if you're targeting an iOS version before 6.0. 当然,如果你创建一个队列,如果你的目标是6.0之前的iOS版本,请不要忘记dispatch_release

Your proposed doCalculationsAndUpdateUIs does data processing and dispatches UI updates to the main queue. 您提议的doCalculationsAndUpdateUIs执行数据处理并将UI更新分派给主队列。 I presume that you have dispatched doCalculationsAndUpdateUIs to a background queue when you first called it. 我假设您在第一次调用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 not wait for the UI update) or synchronously with dispatch_sync (where it will wait for the UI update), the question is why would 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_async as you've used in your original question. 一般来说,你将与异步分派UI更新dispatch_async正如您在你原来的问题使用。 Yes, there certainly are special circumstances where you need to dispatch code synchronously (eg 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. 是的,确实存在需要同步调度代码的特殊情况(例如,您通过在主队列上对其执行所有更新来将更新同步到某个类属性),但通常情况下,您只需调度UI更新异步并继续。 Dispatching code synchronously can cause problems (eg 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. 同步调度代码可能会导致问题(例如死锁),所以我的总法律顾问是,如果有一些迫切的需要,你应该只能同步调度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 doCalculationsAndUpdateUIs multiple 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. 例如,如果您可能多次调用此doCalculationsAndUpdateUIs ,我可能倾向于使用我自己的串行队列而不是并发全局队列,以确保它们不会相互跳过。 Or if you might need the ability to cancel this doCalculationsAndUpdateUIs when the user dismisses the scene or calls the method again, then I might be inclined to use a operation queue which offers cancelation capabilities. 或者,如果您可能需要能够在用户解除场景或再次调用该方法时取消此doCalculationsAndUpdateUIs ,那么我可能倾向于使用提供取消功能的操作队列。 It depends entirely upon what you're trying to achieve. 这完全取决于你想要实现的目标。

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更新回主队列的模式非常常见。

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, ie the queue will be scheduled for execution before any default priority or low priority queue. DISPATCH_QUEUE_PRIORITY_HIGH分派给队列的项目将以高优先级运行,即队列将被调度为在任何默认优先级或低优先级队列之前执行。

DISPATCH_QUEUE_PRIORITY_DEFAULT Items dispatched to the queue will run at the default priority, ie 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_DEFAULT调度到队列的项目将以默认优先级运行,即在调度所有高优先级队列之后但在调度任何低优先级队列之前,将调度队列执行。

DISPATCH_QUEUE_PRIORITY_LOW Items dispatched to the queue will run at low priority, ie the queue will be scheduled for execution after all default priority and high priority queues have been scheduled. DISPATCH_QUEUE_PRIORITY_LOW调度到队列的项目将以低优先级运行,即在调度所有默认优先级和高优先级队列之后,将调度队列以执行。

DISPATCH_QUEUE_PRIORITY_BACKGROUND Items dispatched to the queue will run at background priority, ie 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) (ie disk I/O is throttled and the thread's scheduling priority is set to lowest value). DISPATCH_QUEUE_PRIORITY_BACKGROUND调度到队列的项目将以后台优先级运行,即在调度所有优先级较高的队列后,队列将被调度执行,系统将根据setpriority(2)在具有后台状态的线程上运行此队列中的项目(2)(即磁盘I / O被限制,线程的调度优先级设置为最低值。

No, it won't wait. 不,它不会等待。

You could use performSelectorOnMainThread:withObject:waitUntilDone: . 你可以使用performSelectorOnMainThread:withObject:waitUntilDone: .

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.
  });
});

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
});

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");
       });  
   });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在什么情况下dispatch_async(dispatch_get_main_queue(),^ ...什么都不做 - Under what circumstances does dispatch_async(dispatch_get_main_queue(), ^ … do nothing 调用dispatch_async(dispatch_get_main_queue())时UIView屏幕冻结 - UIView screen freeze when calling dispatch_async(dispatch_get_main_queue()) 从xctest执行dispatch_async(dispatch_get_main_queue() - Execute dispatch_async(dispatch_get_main_queue() from xctest 使用“ dispatch_async(dispatch_get_main_queue(),^ {block})更新UI - Updating the UI using "dispatch_async(dispatch_get_main_queue(),^{block}) NSMutableURLRequest超时后未触发dispatch_async(dispatch_get_main_queue() - dispatch_async(dispatch_get_main_queue() not firing after NSMutableURLRequest with timeoutInterval 嵌套dispatch_async(dispatch_get_main_queue()^ {})的目的是什么? - What is the purpose of nesting dispatch_async(dispatch_get_main_queue()^{}) ? 使用dispatch_async + dispatch_get_main_queue进行UI更新有什么意义? - What's the point in using dispatch_async + dispatch_get_main_queue for UI updates? 直接执行方法与在dispatch_async(dispatch_get_main_queue(),^ {})块中有什么区别 - What is the difference between perform a method directly and in the block of dispatch_async(dispatch_get_main_queue(), ^{}) NSManagedObjectContext中的dispatch_async(dispatch_get_main_queue()在performBlock中 - dispatch_async(dispatch_get_main_queue() inside NSManagedObjectContext performBlock dispatch_async(dispatch_get_main_queue(),^ {}); 从iOS7背景线程慢 - dispatch_async(dispatch_get_main_queue(), ^{}); from Background Thread slow on iOS7
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM