Question

I have an nsthread, a while loop within this one. It's getting objects from a "thread-safe" queue in the main method. When I leave the UIViewController, which contains this nsthread object, I call the nsthread cancel method, but it doesn't stop, because it's locked by the "queueLock" NSCondition. When I go back to this UIViewController a new nsthread will be created and gets the objects form the queue, but the previous thread still exits and both of them try to use the same object from the queue, and this causes a memory management problem. My question is how should I stop this thread, when I leave the UIViewController.

NSThread main method:

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
while ([self isCancelled] == NO) {
    RenderRequest *renderRequest = [queue get];
    [self doRender:renderRequest];
    [renderRequest release];    
}

[pool drain];

This is the get method of the queue class:

- (id) get {
    id toRet = nil;
    [queueLock lock];
    @try {
       while ([queueContents count] == 0) {
            [queueLock wait];
        }

        toRet = [queueContents lastObject];
        [queueContents removeLastObject];
    }

    @finally {
         [queueLock unlock];
         return toRet;
    }
}

Thanks!

Was it helpful?

Solution

I wrote a simple demo, hope this can help you :)

demo.h

#import <Foundation/Foundation.h>

@interface test : NSObject
{
    NSCondition *queueCondition;
    NSThread *queueThread;

    NSMutableArray *queueTask;

    NSTimer *timer;
}
- (id)init;
@end

demo.m

#import "demo.h"

@interface demo (PrivateMethods)
- (void)threadTest;
- (void)cancelThread;
- (void)addTask;
@end


@implementation demo

- (id)init
{
    self = [super init];
    if (self) {
        if (!queueThread) {
            if (!queueCondition) {
                queueCondition = [[NSCondition alloc] init];
            }

            if (!queueTask) {
                queueTask = [[NSMutableArray alloc] initWithCapacity:5];
            }

            queueThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
            [queueThread start];

            [self performSelector:@selector(cancelThread) withObject:nil afterDelay:10];

            if (!timer) {
                timer = [[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(addTask) userInfo:nil repeats:YES] retain];
            }
        }
    }
    return self;
}

- (void)dealloc
{
    [queueThread release];
    [queueCondition release];
    [queueTask release];
    [timer invalidate];
    [timer release];
    [super dealloc];
}

- (void)threadTest
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    while (![[NSThread currentThread] isCancelled]) {
        [queueCondition lock];
        [queueCondition wait];

        if ([queueTask count] == 0) {
            [queueCondition unlock];
            continue;
        }

        NSString *str = nil;
        while ((str = [queueTask lastObject])) {
            NSLog(@"getTask: %@", [queueTask lastObject]);
            [queueTask removeLastObject];

        }

        [queueCondition unlock];
    }
    NSLog(@"threadTest end");
    [pool drain];
}

- (void)addTask
{
    [queueCondition lock];
    if (!queueTask) {
        queueTask = [[NSMutableArray alloc] initWithCapacity:5];
    }
    [queueTask addObject:@"new task"];
    [queueCondition signal];
    NSLog(@"add: new task");
    [queueCondition unlock];
}

- (void)cancelThread
{
    [timer invalidate];

    [queueThread cancel];

    [queueCondition lock];
    [queueCondition signal];
    [queueCondition unlock];
}
@end

OTHER TIPS

- (id) get
{
    id toRet = nil;
    [queueLock lock];
    @try
    {
        while ([queueContents count] == 0)
        {
            [queueLock wait];
            if ([self isCancelled]) return nil; // stop waiting
        }

        toRet = [queueContents lastObject];
        [queueContents removeLastObject];
    }
    @finally
    {
         [queueLock unlock];
         return toRet;
    }
}

thread main

NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

while ([self isCancelled] == NO)
{
    RenderRequest *renderRequest = [queue get];
    if (renderRequest == nil) break; // should stop
    [self doRender:renderRequest];
    [renderRequest release];    
}

[pool drain];

then you can cancel the thread and notify queueLock to stop waiting

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top