Keeping track of multiple threads on iphone - iphone

I'm trying to do something basic to understand threads with a counter that just increments when a button is pressed, and every time the button is pressed, a new thread incrementing the same counter starts. Then I have a stop button to stop a thread that is running. How can I tell how many threads, or which thread is running? Here is my basic template I am working on. Thanks.
-(int)count {
return count;
}
-(void)setCount:(int) value {
count = value;
}
-(void)updateDisplay {
countLabel = [NSString stringWithFormat:#"%i", count];
count++;
}
-(void)myThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)startThread {
[self performSelectorInBackground:#selector(myThread) withObject:nil];
}
-(void)myThreadStop {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)stopThread {
[self performSelectorInBackground#selector(myThreadStop) withObject:nil];
}

Basically, you want to keep track of the number of threads you have running, and also assign each thread a unique ID. Assuming that startThread is an event handler for your button, you might have something like:
static int threadIndex = 0;
static int threadsRunning = 0;
-(void)startThread {
NSNumber* threadId = [NSNumber numberWithInt:threadIndex++];
threadsRunning++;
[self performSelectorInBackground:#selector(myThread) withObject:threadId];
}
Then when you stop a thread, you just decrement threadsRunning.
Looking at your code, though, I'm confused by your stopTread method, since it seems to be doing the exact same thing as the myThread method, i.e., not stopping the thread at all.

You're performing stuff in the background, which is distinct from explicitly creating threads (e.g. it might reuse threads in a thread pool).
If you want really inefficient threading code, you could use something like this:
NSThread * thread = [[[NSThread alloc] initWithTarget:self selector:#selector(myThread) object:nil] autorelease];
[thread start];
while ([thread isExecuting])
{
NSLog(#"Still running");
[NSThread sleepForTimeInterval:0.1];
}
EDIT: If you're actually going to do iPhone development, I recommend looking at NSOperation/NSInvocationOperation/NSBlockOperation instead. Thread management is a real pain to get right.

Related

View not Updating

Kinda new to iPhone programming and was experimenting with threads
- (void)viewDidLoad {
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(changeMain) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:#selector(changeThread) toTarget:self withObject:nil];
}
- (void)changeMain{
NSAutoreleasePool* arp = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < 1000000; i++) {
[mainValue setText:[NSString stringWithFormat:#"%d",i]];
[self.view setNeedsDisplay];
}
[arp release];
}
- (void)changeThread{
NSAutoreleasePool* arp = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < 1000000; i++) {
[threadValue setText:[NSString stringWithFormat:#"%d",i]];
[self.view setNeedsDisplay];
}
[arp release];
}
mainValue and threadValue are both just UILabels. I expected this to run and see both labels run up to 999999 but instead it starts at some low number (what it is when the screen initally refreshing i assume), pauses for a bit, then updates to 999999. I'm thinking the screen just isn't refreshing.
Is this correct? Am I doing it wrong?
You have to perform any Cocoa Touch operations in main thread, in other case results are unpredictable.
You don't have to call setNeedsDisplay manually.
So I'd recommend to use the following construction:
[threadValue performSelectorOnMainThread:#selector(setText:) withObject:[NSString stringWithFormat:#"%d",i] waitUntilDone:YES];
Additional notes:
1. 100000 runs can overflow the main thread queue so some values will disappear
2. You can use waitUntilDone:NO too
The setNeedsDisplay message triggers a redraw, but it only happens during the next time the main thread becomes active. So your side threads trigger a million redraws but they are queued. As soon as the main thread continues, it "collapses" all requests into one redraw.
Most likely setNeedsDisplay just sets a flag that is checked once during each run of the main loop, so setting it 1000000 to true doesn't influence anything. Try to let the "worker threads" sleep after each iteration to give the main thread some time to redraw.
Don't use for() for animations. The for will process in the same "frame". Maybe just have an ivar i and in changeMain you can have if (i<10000) { mainValue.text = [NSString stringWithFormat:#"%d",i]; i++;} or something like that. This way the setText only happens once per "frame".
I'm not sure if this will work, but you could try to force the setNeedsDisplay method to be executed on the main thread, using e.g. [self performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:YES]. This should (hopefully, i didn't test it!) update the view after every increment. You could also try to set waitUntiDone:NO, but I'm unsure what will happen then.
See here

Is calling -setNeedsDisplay from a background task asking for trouble?

I have a background task that updates a view. That task calls -setNeedsDisplay to have the view drawn.
This works:
- (void) drawChangesTask;
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pixels) {
drawChanges((UInt32 *) origPixels, (UInt32 *) pixels, CGBitmapContextGetBytesPerRow(ctx)/4, CGBitmapContextGetHeight(ctx), count--);
if (count < 0) {
count = 150;
}
else
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil ];
[self performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:NO ];
}
[pool release];
}
This does not work:
- (void) drawChangesTask;
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pixels) {
drawChanges((UInt32 *) origPixels, (UInt32 *) pixels, CGBitmapContextGetBytesPerRow(ctx)/4, CGBitmapContextGetHeight(ctx), count--);
if (count < 0) {
count = 150;
}
else
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil ];
[self setNeedsDisplay];
}
[pool release];
}
Anyone know why? When I say it doesn't work, I mean that it runs tens of iterations, sometimes I see portions of my image shifted up or down, or entirely blank, and then the deugger give me an “EXC_BAD_ACCESS” somewhere in CoreGraphics.
Also, if I don't handle the autorelease pool myself, then I get leaking error messages. Don't understand why that is either. My drawChanges() doesn't create any new objects. Here's the error:
2009-08-17 11:41:42.358 BlurApp[23974:1b30f] *** _NSAutoreleaseNoPool(): Object 0xd78270 of class NSThread autoreleased with no pool in place - just leaking
UIKit simply isn't thread-safe — you need to call methods that update UIKit controls on the main thread.
I think that this line:
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil];
Is causing trouble. Have you tried simply calling it again on the current thread? If you need the runloop to execute between the calls, use:
[self performSelector:#selector(drawChangesTask) withObject:nil afterDelay:0.0];
This will call the method on the current thread after the method you're in has finished and the runloop has gone round once.
Problem here is that UIKit is not thread safe, if you tell your UI to do something from a background thread nothign is guaranteed, what you want to do is use the performSelectorOnMainThread method to do updates t o your UI elements

NSOperationQueue not reusing thread on iPhone

I'm using iPhone SDK 3.1.2, and the following code shows the NSOperationQueue does not reuse the thread for each task.
The code does not have any problems on Snow Leopard.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
for(int i = 0; i < 100; i++) {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(run) object:nil];
[queue addOperation:op];
[op release];
}
}
- (void)run {
static int tc = 0;
if([[NSThread currentThread] isMainThread]) {
NSLog(#"MAIN THREAD");
return;
} else if([[NSThread currentThread] name] == nil) {
[[NSThread currentThread] setName:[NSString stringWithFormat:#"THREAD_%d", tc++]];
}
NSLog(#"%#", [[NSThread currentThread] name]);
}
The output shows it create 100 threads to execute the 100 tasks.
2010-01-07 11:46:03.502 OperationQueueTest[7911:4503] THREAD_0
2010-01-07 11:46:03.506 OperationQueueTest[7911:4d03] THREAD_1
2010-01-07 11:46:03.507 OperationQueueTest[7911:4807] THREAD_2
2010-01-07 11:46:03.510 OperationQueueTest[7911:4d07] THREAD_3
2010-01-07 11:46:03.514 OperationQueueTest[7911:5007] THREAD_4
2010-01-07 11:46:03.516 OperationQueueTest[7911:4f0b] THREAD_5
2010-01-07 11:46:03.518 OperationQueueTest[7911:4e0f] THREAD_6
...
2010-01-07 11:46:03.740 OperationQueueTest[7911:4ea7] THREAD_97
2010-01-07 11:46:03.744 OperationQueueTest[7911:4dcf] THREAD_98
2010-01-07 11:46:03.746 OperationQueueTest[7911:460f] THREAD_99
NSOperationQueue is designed to pool and re-use threads in the most efficient way possible and in this instance, it seems it decided not re-using threads was the best way to go.
Test code has it's uses (and it is possible you may have identified a corner case where NSOperationQueue does not do the most efficient thing), but that doesn't mean that NSOperationQueue is always horribly inefficient when dealing with real code in real life; in fact my own experience has been to the contrary.
So I'd say use it in your real code and if you have performance issues, dig further into what it's doing with threads behind the scenes. Otherwise don't worry about it.
As an aside, if you are still curious, you might try recording the names of the threads into an array of NSStrings and then printing everything out at the end of the test code, rather than logging as you go along - this will significantly reduce the amount of work done by each NSInvocationOperation.
Snow Leopard's implementation of NSOperation/NSOperationQueue is now based on GCD.
The iPhone still uses the old Leopard implementation. So you can expect different results on each platform (Not to mention the completely different hardware).
It is possible that spawning new threads is the most efficient way to accomplish the tasks you are giving NSOperationQueue.
NSOperationQueue performs it's added operations asynchronously (in separate thread). So if we print thread information, then it is possible that we will get same thread object most of the time.
NSOperation object added in NSOperationQueue will different but thread objects can be same.
- (void)operaitonqueueTest
{
_opQueue = [[NSOperationQueue alloc] init];
[_opQueue setMaxConcurrentOperationCount:5];
for(int i = 0; i < 50; i++) {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(run) object:nil];
[_opQueue addOperation:op];
}
}
- (void)run {
if([[NSThread currentThread] isMainThread]) {
NSLog(#"MAIN THREAD");
return;
}
NSLog(#"currentThread = %#", [NSThread currentThread]);
}

when is it safe to release an NSThread?

Below is the runloop for my secondary NSThread* processThread
To close the thread I call
//cancel secondary thread
[processThread cancel]
//signal condition
[processCondition broadcast];
Is it then safe to then call:
[processCondition release];
[processThread release];
or do i need to be sure that the thread has finished?
Perhaps like this?
NSTimeInterval timeout = [NSDate timeIntervalSinceReferenceDate] + (1.0/15.0);
while ([processThread isExecuting] && [NSDate timeIntervalSinceReferenceDate] < timeout)
{
[NSThread sleepForTimeInterval: 1.0/1000.0 ];
}
[processCondition release];
[processThread release];
detailed code and explanation:
- (void)processLoop
{
NSAutoreleasePool * outerPool = [[NSAutoreleasePool alloc] init];
[processCondition lock];
//outer loop
//this loop runs until my application exits
while (![[NSThread currentThread] isCancelled])
{
NSAutoreleasePool *middlePool = [[NSAutoreleasePool alloc];
if(processGo)
{
//inner loop
//this loop runs typically for a few seconds
while (processGo && ![[NSThread currentThread] isCancelled])
{
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc]; init];
//within inner loop
//this takes a fraction of a second
[self doSomething];
[innerPool release];
}
[self tidyThingsUp];
}
else
{
[processCondition wait];
}
[middlePool release];
}
[processCondition unlock];
[outerPool release];
}
the combination of:
an inner while loop
NSCondition *processCondition
toggling processGo between YES and NO
allows me to stop and start the inner while loop without cancelling the thread.
if (processGo == YES)
execution enters the inner while loop.
When the main thread sets
processGo = NO
execution leaves the inner while loop and tidys up
on the next pass of the outer loop, execution hits
[processCondition wait]
and waits
if the the main thread resets
processGo == YES
and calls
[processCondition wait]
execution re-enters the inner loop
Yes, it is safe to call release against an NSThread if you are done with it. In non-GC Objective C code the idiom is that once you are done accessing an object you may release it. If anything else needs that object, including the object itself it their job to have a retain against it. In general if an object cannot be safely disposed at arbitrary times it will retain itself while it is in an unsafe state, and release itself when it can be safely disposed of.
This is how things like NSThread and NSURLConnection work (NSURLConnection actually retains its delegate and does a lot of fancy stuff to cope with the retain loop that occurs.

How do I create multiple threads in same class?

all! I want to create multiple threads in my application. I' using following code for creating a thread.
This' buttonPress method where I'm creating a thread:
- (void) threadButtonPressed:(UIButton *)sender {
threadStartButton.hidden = YES;
threadValueLabel.text = #"0";
threadProgressView.progress = 0.0;
[NSThread detachNewThreadSelector:#selector(startMethod) toTarget:self withObject:nil];
}
This' where I'm calling the method for the thread:
- (void)startMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(threadMethod) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)threadMethod {
float actual = [threadProgressView progress];
threadValueLabel.text = [NSString stringWithFormat:#"%.2f", actual];
if (actual < 1) {
threadProgressView.progress = actual + 0.01;
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeMyProgressBarMoving) userInfo:nil repeats:NO];
}
else
threadStartButton.hidden = NO;
}
This thread works properly.
But when I try to create another thread in the same class using the same method, it gets created properly, but at the method "performSelectorOnMainThread", it doesn't execute that method. Can anybody please help me?
It seems that you are trying to queue up methods to be executed on the main thread. You might want to look into an NSOperationQueue and NSOperation objects. If you want to proceed on this path, you might consider changing the repeats parameter to YES. The problem seems to be that the main thread is busy when it's being passed this message. This will cause the main thread to block. You may also consider not using a second threadMethod and calling back to the main thread, but instead wrapping the contents of threadMethod in an #synchronized(self) block. This way, you get the benefits of multi-threading (multiple pieces of code executing at the same time and thus a reactive user interface) without doing some weird stuff with the main thread.
I'm missing the context here. I see a call that creates a new thread, and then I see a call that performs a selector (calls a method) on the main thread..
As I understand, you are calling a function in a new thread (entryMethod), in which you call a method to perform on the main thread (myMethod). I do not understand the point of this, without some background info and possibly some code.
Is it possible that the main thread is busy performing the 'myMethod' function, and thus does not respond to other calls?
Why cant you do it with the same call, by replacing
-(void)startMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(threadMethod) withObject:nil waitUntilDone:NO];
[pool release];
}
with
-(void)startMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
float actual = [threadProgressView progress];
threadValueLabel.text = [NSString stringWithFormat:#"%.2f", actual];
if (actual < 1) {
threadProgressView.progress = actual + 0.01;
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeMyProgressBarMoving) userInfo:nil repeats:NO];
}
else
threadStartButton.hidden = NO;
}
[pool release];
}