Sam
Sam

Reputation: 598

End a while loop that is running in a secondary thread?

I have an application where the user inputs how many times the application should repeat an action. The user inputed value is taken and used in a while loop that repeats the action the user specified amount of times. The process can be very long and I would like to have a button that would allow the user to end the process. I have moved the repeat process to a secondary thread (NSThread) so that the user can continue to interact with the user interface while the process is running. I'm having trouble stopping the repeat process when the user presses the "Stop" button. What is the best way to allow my main thread to communicate to a secondary thread that it needs to stop a while loop running in it? Any help would be greatly appreciated.

Thanks,

Sam

This didn't seem to work for me... I declared two atomic NSNumbers using @property (atomic, retain). They are both set to have an integer value of 0. Here is my loop that runs in the method run in the secondary thread:

 int i = 0;

 while (i < timesToRun && [numberOne intValue] == 0) { 
 [self doSomething]; 
 numberOne = numberTwo;
 i++; 
 }'

My stop button calls a method that changes the integer value of numberTwo to "1". For some reason this isn't stopping the loop. Am I doing something incorrectly?

EDIT: Thank you all for your help. After quite a bit of trial and error, I finally found a solution to my problem. I simply created a .strings file that is empty except for the text "NO". I set the text to "NO" when the application is loaded. In my new while loop, I check to see if the value of the file has changed using an NSString stringWithContentsOfFile. When I want to end the loop I call a method that writes to the file and changes the value to "YES". Here's my new loop:

NSString* path = [[NSBundle mainBundle] pathForResource:@"StopFile" ofType:@"strings"];

NSString *theString = [[NSString alloc] initWithContentsOfFile:path];
NSLog(@"TheString: %@", theString);

int i;

for (i = 0; i < timesToRunScript && theString == [NSString stringWithFormat:@"NO"]; i++) {
    [scriptToRun executeAndReturnError:nil];
   NSString *theString = [[NSString alloc] initWithContentsOfFile:path];
    NSLog(@"string: %@", theString);
    if (theString == [NSString stringWithFormat:@"Yes"]) {
        break;
    }

Please let me know if you see anything that can be improved. Thanks.

Upvotes: 3

Views: 818

Answers (4)

Paolo Bonzini
Paolo Bonzini

Reputation: 1930

Use a single volatile int instead of the two NSNumber objects. The compiler is probably optimizing away numberOne = numberTwo, thinking that it cannot change during the execution of the loop, or something like that.

Upvotes: 0

Rudy Velthuis
Rudy Velthuis

Reputation: 28806

Instead of an NSNumber, use something simple like an atomic int or BOOL property as a termination marker. Those are not reference types so you don't run any risk of memory issues:

@property(assign) BOOL terminateASAP;

And in your loop do:

for (i = 0; i < timesToRun && !terminateASAP; i++) 
{ 
    [self doSomething]; 
    // etc...

    // if necessary, e.g. because each iteration is slow,
    // add more checks for terminateASAP:
    if (terminateASAP)
        break;

    // more slow code...
}

FWIW, the line

numberOne = numberTwo;

looks suspicious. That only copies the pointer, and leaves the old object to which numberOne pointed inaccessible, i.e. you'd have a leak.

Upvotes: 0

Bob9630
Bob9630

Reputation: 953

Your best bet here is to check a volatile atomic variable each iteration of your while loop. Your "stop thread" can then set it to a stop value. Your "loop thread" will then check this variable and can stop when it is set.

Upvotes: 0

Doug Kress
Doug Kress

Reputation: 3537

You can add an atomic property on the object performing the while loop, and check the property with each iteration of the loop.

Making the property atomic will make it thread-safe, so that you can change it in one thread, and read it in another.

Upvotes: 5

Related Questions