0

I am having few doubts about Combinations of GCD and queues, in iOS. let me put those here

First. According to IOS6 programming cookbook, here is small code snippet for downloading images using async and sync. and its as below

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async (concurrentQueue, ^ {
UIImage *image = nil;
dispatch_sync(concurrentQueue, ^ { /* download image here */ });
dispatch_sync(dispatch_get_main_queue, ^ { /* show the downloaded image here */ });
});

Second. I need to download few images from Url, meanwhile i need to update the progress view showing how many images have got downloaded. so i am using following code snippet

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async (concurrentQueue, ^ { dispatch_sync(dispatch_get_main_queue, ^ { /* download image here and update progress view here */ });
});

kindly note that i am updating progress view on main thread. But my progress view is getting displayed after all images has been downloaded. i checked other links but was not pretty clear with the answers.not getting the reason for the problem. My code is as below.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{dispatch_sync(dispatch_get_main_queue(), ^{
       alert = [[UIAlertView alloc] initWithTitle:@"Please Wait Downloading reports..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] ;
       prgView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
       prgView.frame = CGRectMake(10, 50, 270, 20);
       [alert addSubview:prgView];
       [alert show];
      prgView.progress=0.0;
       actual= 0.0;

       for(NSInteger i=0; i<[fileLinks count];i++)
       {

        //  [self downLoadFileFromUrl : fileLinks[i]: i ];
           NSLog(@"Url is %@", fileLinks[i]);
           NSLog(@"i value is %d", i);
           NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileLinks[i]]];
           NSString * _filename=  [fileLinks[i] lastPathComponent];
           NSMutableString *filePath = [[NSMutableString alloc] initWithFormat:@"%@", [VSCore getTempFolder]];
           [filePath appendString: _filename] ;
           [imagedata writeToFile: filePath atomically:YES];
           prgView.progress =((float) i /([fileLinks count]));
           NSLog(@" progress value is %f", prgView.progress);

       }
   });
});

so what is problem here? and my other doubt is, if any one wants to hold the user (displaying progress view or activity indicator) while performing long processing task, can't they do that in main thread ? (provided they are displaying progress bar or activity indicator), why we have to use combination of async, sync with concurrent thread or main thread etc etc.

and i am bit confused with, for what scenario i had to use the proper combination of async, sync, main queue , concurrent queue, serial queue. if any one give explains me or provide me good link for understand The combinations of async , sync and 3 queue. that will be great.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
RockandRoll
  • 409
  • 5
  • 23

3 Answers3

2

You should create and update `UIProgressView' on main queue

and do your work on another queue (not main queue)

And while doing your work, if you want to update any object on GUI, you should do it on main queue

You can do like this:

alert = [[UIAlertView alloc] initWithTitle:@"Please Wait Downloading reports..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] ;
    prgView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
    prgView.frame = CGRectMake(10, 50, 270, 20);
    [alert addSubview:prgView];
    [alert show];
    prgView.progress=0.0;
    actual= 0.0;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for(NSInteger i=0; i<[fileLinks count];i++)
        {
            //  [self downLoadFileFromUrl : fileLinks[i]: i ];
            NSLog(@"Url is %@", fileLinks[i]);
            NSLog(@"i value is %d", i);
            NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileLinks[i]]];
            NSString * _filename=  [fileLinks[i] lastPathComponent];
            NSMutableString *filePath = [[NSMutableString alloc] initWithFormat:@"%@", [VSCore getTempFolder]];
            [filePath appendString: _filename] ;
            [imagedata writeToFile: filePath atomically:YES];

            dispatch_async(dispatch_get_main_queue(), ^{
                prgView.progress =((float) i /([fileLinks count]));
            });
            NSLog(@" progress value is %f", prgView.progress);
        }
    });

You can read an ebook: "Pro.Multithreading.and.Memory.Management.for.iOS.and.OS.X" (buy or ask gg)

Good luck!

Tony
  • 4,311
  • 26
  • 49
  • Thanks for the Answer NtVietHung, But Creating async inside for loop wont be creating that much number of threads? is it ok to create async inside for loop? – RockandRoll Jul 29 '13 at 13:58
  • 1
    No... it wont create any thread. This command: `dispatch_async(dispatch_get_main_queue(), ^{...})` will call `main queue`. You can debug when app's running to see how many thread there are :D – Tony Jul 30 '13 at 09:53
  • It will create _one_ thread which is mostly blocked - since `dataWithContentsOfURL:` is basically an asynchronous task wrapped into a synchronous method. This is a notorious anti-pattern. I would strongly recommend to switch to an asynchronous pattern when dealing with network requests and if you want to improve your code and want to be more friendly to the device's restricted capabilities. – CouchDeveloper Jul 31 '13 at 16:47
0

Combination of async and sync is right, but combination of main thread and other thread cause cycle, especially in async thread using sync main thread.

Qijin
  • 307
  • 2
  • 13
0

If you take a closer look at your code you'll notice you're actually running everything in the main thread.

^{dispatch_sync(dispatch_get_main_queue(), ^{
       alert = [[UIAlertView alloc] initWithTitle:@"Please Wait Downloading reports..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] ;
       prgView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
       prgView.frame = CGRectMake(10, 50, 270, 20);
       [alert addSubview:prgView];
       [alert show];
      prgView.progress=0.0;
       actual= 0.0;
       for(NSInteger i=0; i<[fileLinks count];i++)
       {

        //  [self downLoadFileFromUrl : fileLinks[i]: i ];
           NSLog(@"Url is %@", fileLinks[i]);
           NSLog(@"i value is %d", i);
           NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileLinks[i]]];
           NSString * _filename=  [fileLinks[i] lastPathComponent];
           NSMutableString *filePath = [[NSMutableString alloc] initWithFormat:@"%@", [VSCore getTempFolder]];
           [filePath appendString: _filename] ;
           [imagedata writeToFile: filePath atomically:YES];
           prgView.progress =((float) i /([fileLinks count]));
           NSLog(@" progress value is %f", prgView.progress);

       }
   });

This is going to run the downloadFileFromUrl and the update of the progress view in the main thread (unless the downloadFileFromUrl is an async operations, which I don't know)

Basically you need to run the downloadFileFromUrl method in a secondary thread, then only run this bit on the main thread: prgView.progress =((float) i /([fileLinks count]));

  • Oh... You shouldnt do this. By this way, main queue will be busy while your work is doing. Your GUI will freeze and you will never see changing of "prgView.progress" – Tony Jul 29 '13 at 09:53
  • just to make something clear, the code I wrote is NOT a correct solution! It is just the code in the question with the initial call to dispatch_async removed. I only added for clarity sake. – David Carvalho Jul 29 '13 at 15:42
  • dispatch_sync(dispatch_get_main_queue() - causes a deadlock. Never call dispatch_sync in main queue! http://stackoverflow.com/questions/12379059/why-is-this-dispatch-sync-call-freezing – ninjaproger Apr 13 '15 at 14:32