我刚刚建立了一个小的测试项目来测试中央调度中心。我需要使用串行队列。在运行后台任务时,我需要完全挂起、恢复或取消线程。以及:我如何知道创建的队列是否已经在运行?(然后我必须重新启动它)。
这是我第一次使用多线程,所以如果我使用正确的话,能得到一些提示就太好了。我没有找到任何类似的东西,所以如果你能检查我的代码,那就太酷了。我能正确释放对象吗?还有没有进一步的改进?
非常感谢您的帮助和时间。
这是代码或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
Queue.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
发布于 2012-05-16 21:21:58
我相信一般的做法是,你通常不会挂起后台队列,除非你确实需要这样做(例如,如果你创建的队列被允许运行后续的块,那么你在另一个队列中的一些操作就不能正常/很好地操作)。在某些情况下,您可能希望这样做,但通常您不必为此担心。我们大多数人都在创建队列,使用它们,当我们不积极地使用它们时,让它们处于空闲状态(尽管不是挂起它们),当我们再次需要后台队列时,继续使用它,当我们都使用完它们时(即,在可预见的未来,我们不需要后台队列),我们释放它们,在这一点上,我们不再使用旧的队列指针。
至于如何知道它是否被挂起,我认为除了dispatch_debug()
之外,没有其他方法。您正在挂起它,所以您已经知道它是否挂起了,所以可以编写自己的包装器来跟踪您自己的挂起计数。通常,我认为您会在必要时暂停,然后在冲突的前台(或其他)任务完成时恢复,并且可以安全地再次使用后台队列。在这种情况下,确定它是否被挂起更多的是一个学术问题。
为什么需要挂起后台队列?您试图解决的业务逻辑问题是什么?顺便说一句,我假设您知道在该队列中运行的当前块不受挂起的影响。我认为只有排队的(先前排队和等待,或随后排队的)块才会受到影响,而不是当前块。
就你的代码而言,你是在start方法中释放你的队列(例如,一旦队列被清空,它将被异步释放)。只有在不再需要队列的情况下,才应该这样做。如果调用dispatch_release()
(即不使用ARC),则将来对队列变量的任何使用都是不可靠的(当然,除非dispatch_release()
是与dispatch_retain()
一起完成的)。一旦您最终释放它,您甚至可能希望将队列变量设置为nil,这样您就不会想要使用它。坦率地说,我感到惊讶的是,鉴于您在start方法中执行了dispatch_release()
,并且在已经释放队列的情况下能够调用dispatch_suspend()
和dispatch_resume()
而没有出现任何严重异常(除非偶然情况下,提交给该队列的原始代码块尚未完成)。
总之,在您开始追求挂起队列之前(我不确定您是出于好奇心,还是有一些迫切的业务问题需要解决),我建议您先用业务逻辑和相关代码来澄清您的问题。
https://stackoverflow.com/questions/10617993
复制相似问题