开发者

Multi-Threading question in Objective-C 2.0

I have my main application delegate which contains a method that returns an object. This application delegate runs on the main thread.

I also have a NSOperation that gets run on a different thread. As well as wanting to be able to call my app delegate开发者_开发知识库 method on my main thread sometimes, I also need to call it from my NSOperation thread to get the object that it returns. My first question is, if I call this from my other thread...

id newObject = [[[UIApplication sharedApplication] delegate] myMethod];

... will that method be processed on the same thread as the NSOperation, or will it be the same thread (main) as the application delegate is on?

I also want to make sure that the code within myMethod is only called once at a time by either my operation thread or my main thread. Can I just create a NSLock instance var in my application delegate and do something like:

-(id)myMethod {
    [myLock lock];
    myObject = // Get or create my object to return
    [myLock unlock];
    return myObject;
}

Thanks for your help!

Mike


Unless you explicitly write code to cause something to execute on another thread, every method call is going to be executed directly on the thread it was called upon. There is no magic with a method call. You can think of it as having the exact same semantics/ABI as a C function call for the purposes of threading.

Your locking pattern will work fine to ensure exclusive access across threads.

Two additional unrelated notes (because so many people trip over it):

  • declaring a property as atomic has little to do with thread safety. Atomicity only guarantees that you get a valid value, not the correct value (there is a difference).

  • autoreleased objects are never safe to pass between threads. You need an explicit retain on the sending thread and a balancing eventual release on the receiving thread.


Do you absolutely need to perform this invocation on the NSOperation thread, and not simply provide the required object as part of creating the custom operation?

If so, I would recommend not using locks unless performance is critical. If the iPhone supported it, you could use Grand Central Dispatch to get the object onto your thread:

__block id newObject = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
    newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain];
});

For the iPhone, I would be tempted to create a helper method:

- (void)createNewObject:(NSValue *)returnPtr {
    id newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain];
    *(id *)[returnPtr pointerValue] = newObject;
}

And invoke it like so from your NSOperation thread:

id newObject = nil;
[self performSelectorOnMainThread:@selector(createNewObject:)
                       withObject:[NSValue valueWithPointer:&newObject]
                    waitUntilDone:YES];

Actually performing the execution on the main thread has fewer implicit risks.


If you simply need to protect a critical section of code, why not using the Objective-C @synchronized directive? Of course, using NSLock will also work, but you need to explicitly manage the NSLock instance. From the documentation:

Objective-C supports multithreading in applications. This means that two threads can try to modify the same object at the same time, a situation that can cause serious problems in a program. To protect sections of code from being executed by more than one thread at a time, Objective-C provides the @synchronized() directive.

The @synchronized()directive locks a section of code for use by a single thread. Other threads are blocked until the thread exits the protected code; that is, when execution continues past the last statement in the @synchronized() block.

The @synchronized() directive takes as its only argument any Objective-C object, including self. This object is known as a mutual exclusion semaphore or mutex. It allows a thread to lock a section of code to prevent its use by other threads. You should use separate semaphores to protect different critical sections of a program. It’s safest to create all the mutual exclusion objects before the application becomes multithreaded to avoid race conditions.

Listing 12-1 shows an example of code that uses self as the mutex to synchronize access to the instance methods of the current object. You can take a similar approach to synchronize the class methods of the associated class, using the Class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.

Listing 12-1 Locking a method using self

- (void)criticalMethod
{
    @synchronized(self) {
        // Critical code.
        ...
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜