OStack程序员社区-中国程序员成长平台

标题: iphone - 正确使用 Grand Central Dispatch - 启动、暂停、恢复、取消线程? [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-12 20:59
标题: iphone - 正确使用 Grand Central Dispatch - 启动、暂停、恢复、取消线程?

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

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

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

这是代码或 link to the sample project .

ViewController.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 actionselector(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 actionselector(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 actionselector(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 actionselector(cancelQueueButton forControlEvents:UIControlEventTouchUpInside];
    [cancelbutton setFrame:CGRectMake(600, 200, 170, 70)];
    [self.view addSubview:cancelbutton];

}

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

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


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

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



- (BOOL)shouldAutorotateToInterfaceOrientationUIInterfaceOrientation)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



Best Answer-推荐答案


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

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

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

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

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

关于iphone - 正确使用 Grand Central Dispatch - 启动、暂停、恢复、取消线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10617993/






欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) Powered by Discuz! X3.4