What are the different ways for calling my method on separate thread?

Centurion picture Centurion · Nov 28, 2011 · Viewed 12.5k times · Source

I have some data calculation method (let it be "myMethod:"), and I want to move the call to another thread because I don't want to block my main UI functionality. So, started to do some research on how to call my method on another thread. As far as I see, currently, there are a lot of different ways for doing that. Here's a list:

a) using pure threads (available since iOS 2.0):

[NSThread detachNewThreadSelector:@selector(myMethod:) toTarget:self withObject:_myParamsArray];

b) using a simple shortcut (available since iOS 2.0). Available from inherited NSObject but the method belongs to NSThread class too:

[self performSelectorInBackground:@selector(myMethod:) withObject:_myParamsArray];

c) using a new approach of Grand Central Dispatch queues (available since iOS 4.0):

dispatch_async(dispatch_get_global_queue(0, 0),
  ^ {
      [self myMethod:_myParamsArray];
    });

d) somehow, using some classes such as NSOperation, NSBlockOperation or NSOperationQueue, though not sure how exactly to do it (some example would be appreciated)

Currently, I have used case "b" but curious about pros and cons and other related suggestions on that.

UPDATE: e) also found another way for performing similar threading stuff - Run loops. Here's an excerpt from apple docs:

A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.

IMHO, more or less your are dealing with the same task - how to call your method on separate thread for its async operation.

UPDATE2: Already had some experience with NSInvocationOperation and NSOperationQueue and IMHO it's quite convenient. According to Apple docs, GCD and NSOperations are a preferred way for implementing multithreading. And also, NSOperations runs on GCD starting from iOS 4.0. In short, you instantiate NSIvocationOperation (as a call to your method) then instantiate NSOperationQueue and add invocation to the queue. NSOperationQueue is smart thing enough, you can instantiate multiple NSIvocationOperation objects (wrapping your method calls) and them to NSOperationQueue. The rest is assured. NSOperationQueue determines how much parallel threads it need to perform the calls (NSInvocationOperation) and handles it for you. It might execute first call on thread A, then second on thread B, third on thread C and forth on thread B, so you do not have to worry about that. But if you want, you may tell how max threads NSOperationQueue can use for performing calls (for example 1) but I have no need for that. By default, all tasks are performed on other than main thread, so operation queues are asynchronous by default. Also, if you want to perform your method calls (each wrapped in separate NSInvocationOperation) in a strict queue then you can add dependencies and so NSOperationQueue will preserve method call order. Here's an example:

// wrap your method call into NSInvocationOperation object
NSInvocationOperation *currentOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(yourMethodCall) object:nil];

// _sharedOperationQueue is a shared NSOperationQueue 
// get all executing operations from the queue and get the last operation
_lastOperation = [[_sharedOperationQueue operations] lastObject];

// check if _lastOperation is not nil
if (_lastOperation) {

    // if not then add dependency, so the calls would be performed in a queue
    [currentOperation addDependency:_lastOperation];
}

// say - execute my method (operation)
[_sharedOperationQueue addOperation:currentOperation];

_lastOperation = currentOperation; // mark as last operation for adding dependency to the next operation

// the queue will retain invocation operation so you will release
[currentOperation release];

 ..... you can create another NSInvocationOperation and add it to the queue....

As for RUNLOOPs, still, sometimes you will face them, for example when starting/scheduling a timer, or making NSURL connections. IMHO, a runloop might be compared to a queue of tasks executed on one thread. IMHO a runloop is a pointer to a thread that operates as a queue: it has tasks that might throw events and they will be placed at the end of the queue in that thread. By default all tasks in your app run in a single runloop - in a single thread. I'm saying it's a pointer because when your app generates events then the app must know where to put that event (touch event or other delegate callback) for execution. Of course, you should read about runloops for more detailed information because these are just my thoughts.

Answer

Macmade picture Macmade · Nov 28, 2011

Usually, you would prefer the GCD approach.

It's simpler when it comes to synchronization/locking than pure threads (NSThread - pthread), and it may be more accurate in a performance perspective.

When using pure threads, the problem is you may have performance issues, depending on the number of available cores/processors.

For instance, if you have only one core, creating many threads may slow down your application, because the CPU will spend most of its time switching from one thread to another, saving the stack, registers, etc.

On the other hand, if you have a lot of cores available, it may be nice to create a lot of different threads.

This is where GCD helps, as it manages this for you. It will create the appropriate number of threads, based on the available system resources, to guarantee an optimal utilization, and schedule your actions appropriately.

However, for that reason, tasks launched with GCD may not be real-time.

So if you REALLY needs that a detached task runs immediately, use an explicit threads. Otherwise, use GCD.

Hope this will help you : )

EDIT

A note about performSelectorInBackground: it simply creates a new thread. So there's basically no difference with the NSThread approach.

EDIT 2

NSOperation related stuff are a bit different. On Mac OS X, they are implemented using GCD since version 10.6. Previous versions uses threads.

On iOS, they are implemented using threads only.

Reference

All of this is very well explained in the Concurrency Programming Guide. It discusses the GCD and thread approaches, with a lot of details about the uses and implementations.

If you haven't read it already, you should take a look.