Best multithreading approach in Objective C?
Asked Answered
M

3

6

I'm developing an iPad-app and I'm currently struggling with finding the best approach to multithreading. Let me illustrate this with a simplified example:
I have a view with 2 subviews, a directory picker and a gallery with thumbnails of all the images in the selected directory. Since 'downloading' and generating these thumbnails can take quite a while I need multithreading so the interaction and updating of the view doesn't get blocked.

This is what I already tried:
[self performSelectorInBackground:@selector(displayThumbnails:) withObject:currentFolder];
This worked fine because the users interactions didn't get blocked, however it miserably fails when the user taps on another folder while the first folder is still loading. Two threads are trying to access the same view and variables which results in messing up each others proper execution. When the users taps another folder, the displayThumbnails of the currently loading folder should get aborted. I didn't find any way to do this..

NSThreads
I tried this but struggled with almost the same problems as with the first method, I didn't find a (easy) way to cancel the ongoing method. (Yes, I know about [aThread cancel] but didn't find a way to 'resume' the thread). Maybe I should subclass NSThread and implement my own isRunning etc methods? But isn't there any better way or a third (or even fourth and fifth) option I'm overlooking?

I think this is a fairly simple example and I think there is perhaps a better solution without subclassing NSThread. So, what would you do? Your opinions please!

Marte answered 15/7, 2012 at 22:10 Comment(5)
Apparently I can't make an answer which only reads "GCD"Levirate
You are completely wrong about that. Seriously.Fogle
See here why GCD is so cool :) Or even better, watch one of the WWDC sessions :) About your original issue: There are various ways of cancelling or pausing, no matter if you're using threads, GCD or NSOperationQueue. You can sleep, suspend, wait on a lock, cancel entirely, etc. I think NSOperationQueue will be a good solution for you right now because it automatically guides you to a reasonable implementation..Fogle
Apple's Threading Programming Guide directs you on its first page to the Concurrency Programming Guide, which is what you want.Rolandrolanda
.. but definitely do look into the other technologies too! WWDC sessions videos or the Concurrency Guide on Apple's Developer Website are good starting points.Fogle
F
6

NSOperationQueue should work well for this task.

Another option would be plain GCD, however, if you've never worked with it, NSOperationQueue is probably the better choice since it pretty much automatically guides you to implementing things "the right way", has obvious ways for cancellation, etc.

Fogle answered 15/7, 2012 at 22:18 Comment(0)
B
5

You want to use Concurrent NSOperations to download and process images in the background. These would be managed by an NSOperationsQueue. Essentially these operations would be configured to fetch one image per operation, process it, save it in the file system, then message back to the main app in the main thread that the image is available.

There are several projects on github that you can look at that show how to do this - just search github using "Concurrent" or "NSOperation".

iOS has a really nice facility for doing background work. Grand Central Dispatch (GCD) and Blocks, but those don't let you have an object using delegate callbacks - thus NSOperation.

So you need to read up on blocks, GCD, and then look at some open source Concurrent NSOperations code. Using Concurrent NSOperations is not as simple as using blocks.

Bowers answered 15/7, 2012 at 22:19 Comment(0)
B
0

If I had this problem, I would probably go for an approach like this:

  • a single thread that will load the images, and causes the main thread to display results (I'm not a big fan of having thread mess around with GUI objects)

  • when a new directory is requested... well, it depends on how you want to manage things. Basically, a standard queue construct (condition variable and array) could be used for the main thread to tell the thread that "this directory will be needed" by passing it the path name; the thread will check the queue even when it's loading images (like after every image or so), and switch to the new directory whenever one shows up

  • you could make a directory-reader object that keeps all the state, and store this indexed by the path into a dictionary. When a new directory is requested, check that dictionary first, and only create a new object if there's none for this directory. That way, partially loaded directories would stick around until they are needed again, and can continue to load instead of having to start from scratch.

Pseudocode for the thread:

while (forever)
   new element = nil
   if we have an active directory loader
       tell directory loader to load one image
       if false then make directory loader inactive
       lock queue condition
       if queue has elements
          new element = retrieve LAST element (we aren't interested in the others)
          empty queue
          unlock with status "empty"
       else
          unlock queue
   else
       lock queue on condition "has elements"
       new element = retrieve last element
       empty queue
       unlock with status "empty"
   if new element != nil
       if directory loader for new path does not exist
          setup new directory loader for new path
          store in dictionary
          make it the "active" one
       else
          make the current one the "active"

As for the directory loader, it might look something like this:

read one image:
   if there are still images to read:
       read, process and store one
       return true
   else
       performSelectorOnMainThread with an "update GUI" method and the image list as parameter
       return false;

This is just a quick sketch; there's some code duplication in the thread, and the way I wrote it will only update the GUI after all images have been read, instead of making them appear as we read them. You'll have to copy the current image list, or add synchronization if you want to do that.

Beggarly answered 15/7, 2012 at 22:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.