iphone - 正确使用 Grand Central Dispatch - 启动、挂起、恢复、取消线程?

标签 iphone ios multithreading queue 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 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

最佳答案

我认为一般做法是,除非绝对需要,否则您通常不会暂停后台队列(即,如果您创建的队列允许运行后续 block ,则您在另一个队列中运行的某些操作将无法正常/良好运行)。在某些情况下您可能想要这样做,但通常您不必担心。我们大多数人都在创建队列,使用它们,当我们不主动使用它们时让它们闲置(尽管不是挂起它们),当我们再次需要后台队列时以及当我们全部完成时继续使用它有了它们(即在可预见的将来我们不需要后台队列),我们就释放它们,此时我们不再使用旧的队列指针。

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

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

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

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

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

相关文章:

iphone - Cocos2D removeChildByTag 崩溃?

ios - 当我改变方向时,我的 View 的高度和宽度不会改变

iphone - 如何在webview中加载html文件时调用html函数?

c++ - 好友类 'has no member named...'

c++ - 终止 worker

iphone - 使用麦克风作为MTAudioProcessingTap的输入

iOS 使用 Quartz 旋转 UIImage

ios - 将参数从一个 xib 传递到另一个 xib

ios - 计算 .plist 字典项数(iOS SDK)

java - 保证消息异步传送