views:

62

answers:

5

Is there a clean way of joining threads in Objective C much like "Thread.join" in Java? I found the method performSelector:onThread:withObject:waitUntilDone: but the limitation of this is I can't call the "blocking" on a different line because I want to do something like this:

[dispatch Thread A];
[process something on main thread];
[wait for Thread A to finish before proceeding];

Thank you in advance.

+2  A: 

You could use NSCondition signal/wait.

imaginaryboy
+4  A: 

I'm not aware of any Cocoa API to do this, but it wouldn't be too difficult to do with NSThread, pretty easy to do with a lock, and even easier to do with Grand Central Dispatch.

NSThread

NSThread * otherThread = [[NSThread alloc] initWithTarget:self selector:@selector(methodToPerformInBackground:) object:aParameter];
[otherThread start];

//do some stuff

while ([otherThread isFinished] == NO) {
  usleep(1000);
}
[otherThread release];

NSLock

NSLock * lock = [[NSLock alloc] init];

//initiate the background task, which should immediately lock the lock and unlock when done

//do some stuff

[lock lock]; //this will pause until the background stuff unlocks
[lock unlock];
[lock release];

Grand Central Dispatch

dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, dispatch_get_global_queue(), ^{
  //stuff to do in the background
});

//do some stuff

dispatch_group_wait(myGroup, DISPATCH_TIME_FOREVER);
dispatch_release(myGroup);
Dave DeLong
"Warning: The NSLock class uses POSIX threads to implement its locking behavior. When sending an unlock message to an NSLock object, you must be sure that message is sent from the same thread that sent the initial lock message. Unlocking a lock from a different thread can result in undefined behavior." from reference. So this means I can't lock / unlock the NSLock from the dispatched thread?
Manny
Sorry, nevermind, I misunderstood the message. The initial lock and unlock should be on the same thread, in this case your example is perfectly fine. Thanks again.
Manny
+1  A: 

Could you use a lock to do this? In other words something like this (pseudocode)

  1. create an object to lock on, visible to both threads
  2. dispatch thread A; thread A immediately takes the lock and keeps it for its duration
  3. process something on main thread
  4. main thread attempts to take the lock (this will block until Thread A releases it)
  5. after acquiring the lock, main thread releases it and continues on
Brian
+2  A: 

NSConditionLock is the answer to my question, Sorry Dave DeLong, but I cannot use:

  1. "while ([otherThread isFinished] == NO) " -- because I need fast continuous processing and cannot use sleep.

  2. NSLock -- because as you said it "initiate the background task, which should immediately lock the lock and unlock when done", this is not a solution because I tried it and we are not sure if the subthread will execute last before the lock-unlock-release on main thread, I ended up getting random errors.

  3. Grand Central Dispatch --because it's only available in IOS4 and Snow Leopard 10.6, I'm using a lower version.

But your answer gave me the idea and thank you very much for it, so I just "upped" you.

I ended up doing this:

#define T_START 0
#define T_FINISHED 1
-(void) updateVerticalScopeBackground: (id) aParam {   
[lockForThread lock];   
NSAutoreleasePool *pool = [NSAutoreleasePool new];   
//do something
[pool release];   
[lockForThread unlockWithCondition:T_FINISHED];  
}


-(void) sumFunc { 
  lockForThread = [[NSConditionLock alloc]
                                   initWithCondition: T_START];
  NSThread* updateVerticalScope = [[NSThread alloc] initWithTarget:self selector:@selector(updateVerticalScopeBackground:) object:nil];
  [updateVerticalScope start];

  //do some processing

  [lockForThread lockWhenCondition:T_FINISHED];   
  [lockForThread unlockWithCondition:T_FINISHED];   
  [lockForThread release]; 
}
Manny
A: 

You never want your main thread to be blocked waiting for another thread. At least you don't in any application with a user interface because, if the main thread is blocked, your application is frozen.

It would be far better for the main thread to start the background thread, do the other stuff it needs to do and then return to the run loop. The background thread would notify the main thread of completion by sending -performSelectorOnMainThread:waitUntilDone:

JeremyP