2

我刚刚建立了一个小型测试项目来测试 Grand Central Dispatch。我需要使用串行队列。在运行后台任务时,我需要完全暂停、恢复或取消线程。并且:我如何知道创建的队列是否已经在运行?(然后我必须重新启动它)。

这是我第一次使用多线程,因此如果我使用正确的话,得到一些提示会非常好。我没有找到类似的东西,如果你能检查我的代码,那就太酷了。我释放对象对吗?有没有进一步的改进?

非常感谢您的帮助和时间。

这是代码或示例项目的链接

视图控制器.m

#import "ViewController.h"
#import "SVProgressHUD.h"
#import "Queue.h"

@interface ViewController (){
    Queue* queue;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    queue = [[Queue alloc] init];

    UIButton* startbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [startbutton setTitle:@"Start Queue" forState:UIControlStateNormal];
    [startbutton addTarget:self action:@selector(startQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [startbutton setFrame:CGRectMake(100, 200, 100, 70)];
    [self.view addSubview:startbutton];


    UIButton* suspendbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [suspendbutton setTitle:@"Stop Queue" forState:UIControlStateNormal];
    [suspendbutton addTarget:self action:@selector(suspendQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [suspendbutton setFrame:CGRectMake(250, 200, 100, 70)];
    [self.view addSubview:suspendbutton];

    UIButton* resumebutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [resumebutton setTitle:@"Resume Queue" forState:UIControlStateNormal];
    [resumebutton addTarget:self action:@selector(resumeQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [resumebutton setFrame:CGRectMake(400, 200, 170, 70)];
    [self.view addSubview:resumebutton];

    UIButton* cancelbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
    [cancelbutton setTitle:@"Cancel Queue" forState:UIControlStateNormal];
    [cancelbutton addTarget:self action:@selector(cancelQueueButton:) forControlEvents:UIControlEventTouchUpInside];
    [cancelbutton setFrame:CGRectMake(600, 200, 170, 70)];
    [self.view addSubview:cancelbutton];

}

-(void) startQueueButton:(UIButton*) button{    
    NSLog(@"---> startQueueButton");
    [queue start];
}

-(void) suspendQueueButton:(UIButton*) button{
    NSLog(@"---> suspendQueueButton");
    [queue suspend];
}


-(void) resumeQueueButton:(UIButton*) button{
    NSLog(@"---> resumeQueueButton");
    [queue resume];
}

-(void) cancelQueueButton:(UIButton*) button{
    NSLog(@"---> cancelQueueButton");
    [queue cancel];
}



- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

@end

队列.m

#import "Queue.h"
#import "SVProgressHUD.h"

@interface Queue (){
    dispatch_queue_t queue;
}

@end

@implementation Queue


-(void) start{
    NSLog(@"Queue - start");    

    int count = 1000;

     // SERIAL QUEUE ======================================
     // =======================================================================

    queue = dispatch_queue_create("com.jf.TestQueue", NULL);

    [SVProgressHUD showWithStatus:@"Rendering..."];


    for(int i = 0; i < count; i++) 
    {
        dispatch_async(queue, ^{

            NSLog(@"--> ASYNC %d", i);

           // rendering complete, get back to main queue
           dispatch_async(dispatch_get_main_queue(), ^
              {
                 NSLog(@"--> Image rendered: %d", i);

                  if (i == count-1) {
                      NSLog(@"EndRenderingQueue");

                      [SVProgressHUD dismiss];
                  }
              });
        });
    }

    dispatch_release(queue);    // even under ARC we have to release it    
}


-(void) suspend{

    NSLog(@"Queue - suspend");

    if (queue) {
        NSLog(@"___suspend");
        dispatch_suspend(queue);
    }
}

-(void) resume{
    NSLog(@"Queue - resume");
    if (queue) {
        dispatch_resume(queue);
    }
}
-(void) cancel{
    NSLog(@"Queue - cancel");

    if (queue) {
        dispatch_suspend(queue);
        //dispatch_release(queue);  // if it´s uncommented, it crashes. How to release it securely?
        queue = nil;

        [SVProgressHUD dismiss];

    }

}

@end
4

1 回答 1

4

我相信一般做法是您通常不会暂停后台队列,除非您绝对需要(即,如果您创建的队列被允许运行后续块,则您在另一个队列中操作无法正常/正常运行的东西)。在某些情况下您可能想要这样做,但通常您不必担心。我们中的大多数人都在创建队列,使用它们,当我们不主动使用它们时让它们处于空闲状态(虽然不是暂停它们),当我们再次需要后台队列时继续使用它,当我们都完成时有了它们(即在可预见的将来我们不需要后台队列),我们释放它们,此时我们不再使用那个旧的队列指针。

至于如何知道它是否被暂停,我认为除了dispatch_debug(). 您正在暂停它,因此您已经知道它是否已暂停,因此可以编写自己的包装器来跟踪您自己的暂停计数。一般来说,我会认为你会在必要时暂停,然后在冲突的前台(或其他)任务完成并且可以安全地再次使用后台队列时恢复。在这种情况下,确定是否暂停更多的是学术问题。

为什么需要暂停后台队列?您要解决什么业务逻辑问题?而且,顺便说一下,我假设您知道在该队列中运行的当前块不受暂停影响。我相信只有排队的(之前排队等待或随后排队的)块受到影响,而不是当前块。

就您的代码而言,您在 start 方法中释放队列(例如,一旦队列被清空,它将被异步释放)。只有在不再需要队列时才应该这样做。如果您调用 a dispatch_release()(即不使用 ARC),队列变量的任何未来使用都将不可靠(当然,除非dispatch_release()是与 a 一起完成dispatch_retain())。一旦你最终释放它,你甚至可能想要将队列变量设置为 nil,这样你就不想使用它了。坦率地说,我很惊讶,因为你dispatch_release()在你的 start 方法中做了一个你可以调用的方法,dispatch_suspend()并且dispatch_resume()没有一些严重的异常,因为你已经释放了你的队列(除非碰巧,原始代码块提交给该队列尚未完成)。

底线,在您开始寻求暂停队列之前(我不确定您是否出于求知欲或您是否有一些令人信服的业务问题要解决),我建议您澄清您的问题业务逻辑和相关代码。

于 2012-05-16T21:21:58.213 回答