简体   繁体   中英

How does performing time-consuming operations on the main thread affect the UI refresh

Synchronization time on the main thread can cause the main thread to block. Is this the cause of UI lag? If I'm performing asynchronous, time-consuming operations on the main thread, does this affect the flow of the UI on the main thread? Again, time-consuming operations must be placed in child thread asynchronous operations.

  1. Is the view refresh code waiting for a time-consuming operation to complete before it can be executed the reason for the UI not flowing?

     [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil]; - (void)syncMain { dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ // 追加任务1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation NSLog(@"1---%@",[NSThread currentThread]); // on the main thread } }); self.testImage=[UIImage imageNamed:xxx];//Waiting for a time-consuming operation to complete, causing a UI refresh block } 
  2. Does performing asynchronous time-consuming operations on the main thread affect UI fluidity?

     - (void)asyncMain { dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ // 追加任务1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation NSLog(@"1---%@",[NSThread currentThread]); // on the main thread,too } }); self.testImage=[UIImage imageNamed:xxx];//Execute code now } 
  3. Whether time-consuming operations can only be performed asynchronously on child threads to keep the UI intact?

     - (void)asyncChild { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{      [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation        dispatch_async(dispatch_get_main_queue(), ^{ self.testImage=[UIImage imageNamed:xxx];        });    }); } 

Can you give an example of some operations you are having issues with?

If the main thread is busy doing other things instead of UI releated things, then yes, you might experience a slow, or unusable, UI.

As a general rule, time consuming, or synchronous, operations should be performed on background threads to avoid this issue. Please note, however, that UI/UIKit related operations must be performed on the main thread, or your app might crash.

If you are on the main thread and dispatch something to an asynchronous thread it will not have an impact on the UI unless the thread is consuming all the resources on the device.

What will get you though is when your async thread is done and you go to update the UI with the results of the async operation. If you forget to move the UI updates onto the main thread the UI will update at some random time interval, maybe, or it won't update at all. This is what gets most people when they do async operations.

In general, dispatching something asynchronously to a background queue does not have a material impact the UI. That's precisely why we do this, to minimize impact on the UI.

If you're seeing lag, that can happen if:

  • the background thread accidentally performed some UI update, neglecting to dispatch that back to the main thread;

  • the background job dispatched some cascading series of tasks to concurrent global queues, exhausting the very limited worker threads available;

  • the asynchronous job is, itself, using the main thread for anything time consuming, or

  • the background task was so intense as to consume all system resources (which is extremely unusual).


In your edit, you supply some examples:

  1. Is the view refresh code waiting for a time-consuming operation to complete before it can be executed the reason for the UI not flowing?

     [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil]; - (void)syncMain { dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ // 追加任务1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation NSLog(@"1---%@",[NSThread currentThread]); // on the main thread } }); self.testImage=[UIImage imageNamed:xxx];//Waiting for a time-consuming operation to complete, causing a UI refresh block } 

When you dispatch the block back to the main thread, you do not want to include these time consuming tasks, or else you will adversely affect the UX. Do those time consuming tasks on the background thread before you dispatch to the main queue.

[NSThread detachNewThreadSelector:@selector(asyncMain) toTarget:self withObject:nil];

- (void)asyncMain {    
    dispatch_queue_t queue = dispatch_get_main_queue();

    for (int i = 0; i < 2; ++i) {
        [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
        NSLog(@"1---%@",[NSThread currentThread]);      // still on the background thread
    }

    dispatch_async(queue, ^{            
        self.testImage=[UIImage imageNamed:xxx];//Execute code now
    });
}

And there's no reason to synchronously dispatch updates back to the main queue. Dispatch these updates asynchronously Why have the background thread wait for the UI update to finish?

Personally, I'd suggest eliminating the detachNewThreadSelector and just stick with GCD (like your third example):

- (void)someTaskAndUpdateUI {    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
            NSLog(@"1---%@",[NSThread currentThread]); // but on background queue
        }

        dispatch_async(dispatch_get_main_queue(), ^{            
            self.testImage=[UIImage imageNamed:xxx];//Execute code now
        });
    });
}
  1. Does performing asynchronous time-consuming operations on the main thread affect UI fluidity?

     - (void)asyncMain { dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ // 追加任务1 for (int i = 0; i < 2; ++i) { [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation NSLog(@"1---%@",[NSThread currentThread]); // on the main thread,too } }); self.testImage=[UIImage imageNamed:xxx];//Execute code now } 

The fact that you dispatched this asynchronously to the main thread vs synchronously is inconsequential. The issue isn't how you dispatched to the main queue, but rather what you dispatched. The code that you have dispatched to the main thread has some slow task, meaning the main thread cannot do anything else while that slow task happens. That's what adversely impacts the UI.

So the solution is just like above, where you want to perform those time consuming tasks before your dispatch to the main queue. And while it doesn't matter from the main thread's perspective whether you dispatch synchronously or asynchronously, we'd generally prefer asynchronously. Again, why does the background thread need to sit there waiting for the UI update to finish.

  1. Whether time-consuming operations can only be performed asynchronously on child threads to keep the UI intact?

     - (void)asyncChild { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation dispatch_async(dispatch_get_main_queue(), ^{ self.testImage=[UIImage imageNamed:xxx]; });    }); } 

Correct. And I would advise you use this third pattern, and completely excise the detachNewThreadSelector from your code. Stick with GCD.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM