Iphone 如何在iOS上取消ApplicationIdentinterBackground中的工作线程

Iphone 如何在iOS上取消ApplicationIdentinterBackground中的工作线程,iphone,multithreading,user-interface,Iphone,Multithreading,User Interface,我已经仔细研究了一个合适的解决方案,但没有成功: 我的iPhone应用程序根据用户需求通过NSURL请求更新数据。在线加载的每个文件的大小只有1.5k,但一次更新可以包含400个这样的文件。因此,我在一个单独的线程中进行下载,该线程是可取消的,在更新过程中,有一个UIAlertView,带有进程指示和取消按钮。该设备可能会运行1…3分钟,因此它可能会超过设备保持活动的超时时间,或者发生其他事情,我的应用程序将进入后台 当调用ApplicationIdentinterBackground时我什么也

我已经仔细研究了一个合适的解决方案,但没有成功: 我的iPhone应用程序根据用户需求通过NSURL请求更新数据。在线加载的每个文件的大小只有1.5k,但一次更新可以包含400个这样的文件。因此,我在一个单独的线程中进行下载,该线程是可取消的,在更新过程中,有一个UIAlertView,带有进程指示和取消按钮。该设备可能会运行1…3分钟,因此它可能会超过设备保持活动的超时时间,或者发生其他事情,我的应用程序将进入后台

当调用ApplicationIdentinterBackground时我什么也不做,我意识到我的应用程序被挂起,工作线程也被挂起。当应用程序再次出现在前台时,它会唤醒并继续工作。到现在为止,一直都还不错。当我再次出现在前台后按下cancel(取消)按钮时,我遇到了麻烦——然后线程会做出被取消的反应(正如它应该做的那样),但立即再次运行,并在最后崩溃(在Apple框架的深处有错误代码)。只要应用程序保持在前台,它就可以完美地取消线程,因此我的想法是在输入ApplicationIdentinterBackground时停止/取消线程。我已经尝试了一些方法,但是每次尝试都会导致工作线程在调用ApplicationIdentinterBackground时挂起,因此我无法取消线程并等待。我试过的一个例子是:

diagView*   viewDiagCtrl = startDiag.diagViewControl;

if (viewDiagCtrl != nil && viewDiagCtrl.actionThread != nil)
{
    // UIBackgroundTaskIdentifier bgTask is instance variable
    NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);
    bgTask = [application beginBackgroundTaskWithExpirationHandler:
             ^{
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    NSLog(@"stopping actions definitely");
                    [viewDiagCtrl stopBackGroundActivity:self];
                    [application endBackgroundTask:self->bgTask];
                    self->bgTask = UIBackgroundTaskInvalid;
                  });
              }];
    dispatch_async(dispatch_get_main_queue(),
    ^{
        // Start with 10ms time boxes
        NSTimeInterval  ti = 0.01;

        while ([application backgroundTimeRemaining] > 1.0)
        {
            if (![viewDiagCtrl.actionThread isExecuting])
                break;

            NSDate* date = [NSDate dateWithTimeIntervalSinceNow:ti];

            // Let the current run-loop do it's magif for one time-box.
            [[NSRunLoop currentRunLoop] runMode: NSRunLoopCommonModes
                         beforeDate: date];

            // Double the time box, for next try, max out at 1000ms.
            ti = MIN(1.0, ti * 2);
        }

        [viewDiagCtrl stopBackGroundActivity:self];
        [application endBackgroundTask:self->bgTask];
        self->bgTask = UIBackgroundTaskInvalid;
     });
}
我对整个队列没有经验,在某处发现了这样的构造。我假设所有在主线程和工作线程中分派的工作都保持挂起状态,因此我没有任何机会取消它。有什么好主意吗


我也读过关于在没有多线程的情况下做任何事情的尝试,但我并不十分欣赏。是否有一些有用的链接可以正确处理“转到后台”的情况?

您可以在后台线程中包装您的线程工作,如:

...

[self performSelectorInBackground:@selector(backgroundThread)
                       withObject:nil];

...


-(void)backgroundThread
{
    // do a download once a minute in a background thread - dont let the system suspend us 
    while(true)
    {
        BOOL expire = NO;    
        bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{expire = YES;}];
        while(!downloadComplete && !expire)
        {
             //do your multiple file downloads here;
        }
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];

        [NSThread sleepForTimeInterval:60];
    }
}
这样做的目的是继续执行正在进行的任务,当应用程序处于后台时,该任务将继续执行-我在我的线程密集型/网络密集型应用程序中有几个地方这样做

AFAIK:你不必等到应用程序被后台处理后才调用beginBackgroundTask——你应该在应用程序有需要完成的功能时调用它,而不会被中断/挂起

我还将NSURLRequest用于:

[request setNetworkServiceType:NSURLNetworkServiceTypeVoIP];

(并在supportedbackgroundmodes标志中使用voip后台模式)

我认为GCD不是进行网络编程的最佳方式。尝试使用NSURLConnection委托接口等异步下载数据。这降低了应用程序的复杂性,避免了多线程编程的麻烦。我还没有使用NSURLConnection,其余的都已经完全实现了。如果没有别的路,我就走这条路。目前的情况是,由于演示数据暂时完全无效,我必须为整个更新过程阻止用户I/O。因此,我只允许GUI根据需要进行更新,并通过UIAlertView锁定用户交互。线程本身是用[[NSThread alloc]initWithTarget:selector:object:创建的,尽管后台挂起,但其所有处理工作正常。谢谢,确实如此。事实上,再次转换到前台时会发生一些奇怪的事情,但我想我可以解决它。