2012-03-28 33 views
3

我有一个nsthread,在这个之内的while循环。它从主方法的“线程安全”队列中获取对象。当我离开包含此nsthread对象的UIViewController时,我调用nsthread cancel方法,但它不会停止,因为它被“queueLock”NSCondition锁定。当我回到这个UIViewController时,会创建一个新的nsthread,并将这些对象组成队列,但是前一个线程仍然退出,并且它们都尝试使用队列中的同一个对象,这会导致内存管理问题。我的问题是当我离开UIViewController时应该如何阻止这个线程。iOS - Objective-C - 如何在等待时停止NSThread?

NSThread主要方法:

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

[pool drain]; 

这是队列类的get方法:

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

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

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

谢谢!

回答

1

我写了一个简单的演示,希望这可以帮助你:)

演示.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 
+0

谢谢,我会试一试。 – 2012-03-29 20:29:33

+1

感谢您的写作!我用这个很好的演示来了解NSThread以及如何排队数据。虽然现在每个人都指向GCD,但我认为记住NSThread'亲手做'选项很重要。 – LEO 2013-09-15 01:58:34

0
- (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; 
    } 
} 

线程主要

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]; 

那么你就可以取消该线程,并通知queueLock停止等待

+0

谢谢。我尝试了这一点,但第二次打开UIViewController我得到一个“[不是类型发布]:消息发送到释放”错误在[pool drain]行,并且第一个线程仍然存在。我在UIViewController的viewDidDisappear方法中调用线程cancel方法。 – 2012-03-29 13:39:44