1

The ideal way to update UI using background thread is

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

But we can update UI even without using main queue using

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    //Run UI Updates
});

So I am using beizer path to draw some points and those points are coming from iPad UIPanGestureRecognizer. Now I draw those points on main thread and also rotate those points to get new points and draw those new points using background thread(concurrent). Here is my code:

CGPoint touchPoint = [sender locationInView:self.view];
[pencilLayer[0] addPoint:touchPoint];
for(int i = 1; i < 4; i++)
{
    dispatch_async(privateQueue, ^{
        CGPoint point = Rotatepoint(500, 500, 45(degree), touchPoint);
        [pencilLayer[i] addPoint:point];
    });
}

My Question is: The main thread and private queue should draw on UI simultaneously. Why after releasing the gesture, privateQueue draw points on UI?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • It's not really clear what are you trying to do.. are you drawing with your point and running on private queue the code that should draw the line based on the gesture? But the line only appears after you stop touching the screen? – Milan Nosáľ Feb 01 '18 at 10:54
  • Yes, i got points from gesture and draw those points on background thread. But after releasing the gesture, background thread then draw points on UI – GAURAV JAIN Feb 01 '18 at 10:59
  • You must keep the updating of the UI on the main thread, like in your first example. Your last example not only fails to do that, but is also introduces yet another problem, namely the updating a property concurrently from multiple threads. (If you run this through the Thread Sanitizer, TSan, it would likely bring this problem to your attention.) You must synchronize access to property when accessing it simultaneously from multiple threads. Frankly, I’m not seeing anything here that warrants the introduction of GCD at all.... – Rob Feb 01 '18 at 11:53

2 Answers2

2

Well, according to the docs usually it is best to keep code that manipulates UI on the main thread:

Threads and Your User Interface

If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but even for those that do not, keeping this behavior on the main thread has the advantage of simplifying the logic for managing your user interface.

Now in your specific case it might be exactly the reason why happens what happens for you. But anyway, in your particular case I see no good reason to perform drawing on a private queue. But there are reasons why you should keep it on main thread - the code manipulates UI, so it is recommended to keep it on main thread (see reference above). Moreover, the point drawing is NOT a performance exhaustive operation - there is no good reason to put it on background.

So I recommend removing that dispatch on private queue and simply use this:

CGPoint touchPoint = [sender locationInView:self.view];
[pencilLayer[0] addPoint:touchPoint];
for(int i = 1; i < 4; i++)
{
    CGPoint point = Rotatepoint(500, 500, 45(degree), touchPoint);
    [pencilLayer[i] addPoint:point];
}

Also, check this blog entry about threading and UI.

Community
  • 1
  • 1
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • In for loop, if we increase number of instances(4 in this case) then performance degrades with time. Any Solution on how to improve performance?? – GAURAV JAIN Feb 01 '18 at 11:08
  • @GAURAVJAIN hm.. that makes things a bit more complicated.. nothing comes to my mind that would be general enough - but I would probably try to experiment to find a workaround - like do you need to draw the same point to all the layers? – Milan Nosáľ Feb 01 '18 at 11:15
  • @GAURAVJAIN I would also try to put the whole for loop asynchronously on the main thread, just to see what happens – Milan Nosáľ Feb 01 '18 at 11:16
  • @GAURAVJAIN - I’d suggest you edit your question to include MCVE that manifests your “degrading performance with time”. This is XY problem, in which you’re asking for feedback on your proposed solution to a problem that you haven’t clearly articulated. Let us help you solve your original problem rather than soliciting advice on your proposed solution to that problem... – Rob Feb 01 '18 at 11:58
  • Yes your solution is effective when n=4, but as n increases, after some time this get slow – GAURAV JAIN Feb 01 '18 at 11:58
0

You add an item for every point to the background queue. This should be not quite performant, and may lead to lags. It's just better to add one single item to the background queue:

CGPoint touchPoint = [sender locationInView:self.view];

dispatch_async(privateQueue, ^{
    [pencilLayer[0] addPoint:touchPoint];
    for(int i = 1; i < 4; i++)
    {
            CGPoint point = Rotatepoint(500, 500, 45(degree), touchPoint);
            [pencilLayer[i] addPoint:point];
    }
});

This will draw all points at once.

clemens
  • 16,716
  • 11
  • 50
  • 65
  • What difference does it make except that CGPoint point = Rotatepoint(500, 500, 45(degree), touchPoint) calculation is being done on main thread. – GAURAV JAIN Feb 01 '18 at 11:56
  • @GAURAVJAIN: The complete calculation is done in the background and the main thread isn't blocked. Each call of `dispatch_async` requires some time from the main thread, and each execution of a queue item has a litte lag to the previous item. – clemens Feb 01 '18 at 12:13