Re: A couple NSRunLoop questions
Re: A couple NSRunLoop questions
- Subject: Re: A couple NSRunLoop questions
- From: Ken Thomases <email@hidden>
- Date: Mon, 4 May 2009 22:12:49 -0500
On May 4, 2009, at 8:54 PM, Miles wrote:
1) I have a timer on a run loop. Every so often I invalidate the
timer which
as I understand it should remove the run loop as well. At a later
time I
start a new run loop (by calling the same method as before), then
invalidate
that timer after a while, and repeat.
I think you are confused about the nature of run loops.
Every thread has exactly one run loop associated with it. You don't
create run loops nor do you remove them.
You can schedule a timer on a run loop. Invalidating the timer
removes it from the run loop. This doesn't affect the lifetime of the
run loop. The run loop existed before and continues to exist after.
When I pause the debugger after this happens a few times I notice that
sometimes the run loop threads have been successfully removed and
sometimes
not. I should only ever see one at most, but sometimes I see two or
more.
The timer in the extra threads hanging around are not running
because it
would be obvious in my app if that was happening.
Again, you are confused. There's no such thing as a "run loop
thread". There are threads. They all have a run loop. It is not
true that there should only ever be one. There should be as many
threads as the program and/or frameworks are using. However, a thread
may or may not be actively running its run loop at any given time. If
you see threads whose stack traces show run loop-related functions,
then those functions are running their run loops. In all probability,
they are blocked in their run loop waiting for something of interest
to happen. This is normal.
These threads were not necessarily created directly by you (and
definitely weren't created by your timer). Some may have been created
by the frameworks for their own purposes.
Your timer exists from the time you created it until all ownership
responsibilities for it are released. When you schedule a timer on a
run loop, the run loop retains it. When you invalidate the timer, the
run loop releases it. You may also have an ownership responsibility,
depending on how you created the timer and if you retain it
explicitly. Seeing other threads in the debugger, even other threads
running their run loops, in no way indicates that your timer still
exists.
Here's the important code:
[NSThread detachNewThreadSelector:@selector(startTileAnimation:)
toTarget:animationHelper withObject:nil];
So here you're actually creating a new thread. That's above and
beyond what you previously discussed with timers.
-(void)startTileAnimation:(id)sender {
NSLog(@"start loop");
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc]
init];
myRunLoop = [NSRunLoop currentRunLoop];
animationTimer = [NSTimer
scheduledTimerWithTimeInterval:ANIMATION_STEP_FREQUENCY target:self
selector:@selector(doTileAnimation:) userInfo:nil repeats:YES];
[myRunLoop addTimer:animationTimer forMode:NSDefaultRunLoopMode];
[myRunLoop run];
[autoreleasepool release];
}
The only thing this thread is doing is letting a timer fire. Is there
a reason you're not doing this on the main thread?
Also, you seem to be working with GUI stuff. You can do some GUI
stuff from background threads, but it presents some complications.
Again, you're probably better off doing this on the main thread.
-(void)stopTileAnimation {
if(animationTimer != nil) {
NSLog(@"stop loop");
[animationTimer invalidate];
animationTimer = nil;
}
}
There's no guarantee that removing all of _your_ input sources and
timers from a run loop will cause -[NSRunLoop run] to exit. See the
documentation for that method.
Also, if you're invoking -stopTileAnimation from a thread other than
the one with your timer on it, that's not safe. There's a pretty
clear warning about exactly this in the documentation for -invalidate.
If you're seeing your threads sticking around after you've invoked -
stopTileAnimation, these problems are probably why.
2) My second question is, from within that run loop, is it safe to
make a
copy of an array that’s in the main thread like this?
NSMutableArray *tmp = nil;
NSLock *theLock = [[NSLock alloc] init];
if( [theLock tryLock] ) {
tmp = [NSArray arrayWithArray:animatingTilesArray];
[theLock unlock];
}
No, that's not how locks work. In order to synchronize access to a
given object between threads, those threads need to lock the _same_
lock. The above code creates a new lock, which, since it's local,
can't possibly be shared with anything else. Therefore, that lock is
useless for synchronizing anything.
Typically, the lock would have a similar scope and lifetime to the
array object which it's guarding. That is, if animatingTilesArray is
an instance variable, the lock would also be one. Then, _all_ access
to the array would have to be guarded by the lock. You can't just
lock the lock when you copy from the array, you have to lock it when
you mutate the array, or enumerate the array, or pick a specific
element from it, etc. A good way to do this is to create a class
specifically to manage the array and all accesses to it. An even
better way would be to avoid accessing this array from multiple
threads in the first place.
Don't take this the wrong way, but you seem to be in over your head.
Threading is a very complicated topic. If you can avoid it, you
should. If you have to use it, you should read all of the basic
introductory material you can get your hands on, and wade in slowly
rather than diving it.
Cheers,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden