iphone - dispatch_queue_t 仍然阻塞主线程

标签 iphone objective-c multithreading ios

我想触发一个方法并让它在后台运行——我不关心它启动后到底发生了什么。

所以在我的主 viewDidLoadMethod 中,我有我所有的常用代码和这个:

 dispatch_queue_t newImages = dispatch_queue_create("load image in background", NULL);
    dispatch_async(newImages, ^{
        [self getNewImages];
    });
    dispatch_release(newImages);

我的假设是创建队列,然后将函数调用设置为在后台线程中运行,我的应用程序将继续运行。事实并非如此。

是从该函数调用的所有内容都自动移动到后台线程,还是我现在需要确保没有其他可能的阻塞调用发生。

编辑 - 阻塞的代码:

-(void) getNewImages
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0];
    NSString *lastImagesSyncDate = [defaults valueForKey:@"ImagesLastSyncDate"];

    dispatch_queue_t newImages = dispatch_queue_create("com.mydomain.app.newimagesinbackground", NULL);
    dispatch_async(newImages, ^{

        for (Manufacturer *m in self.manufacturers)
        {
            NSString *myurl = [NSString stringWithFormat: kNewImagesURL, m.ManufacturerID  ,lastImagesSyncDate];
            NSString *manufacturerID = [m.ManufacturerID stringValue];
            NSURL *url = [NSURL URLWithString:[myurl stringByReplacingOccurrencesOfString:@" " withString:@"%20"]];
            __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
            [request setCompletionBlock:^{

                // Use when fetching text data
                NSString *responseString = [request responseString];
                NSString *fileName =[documentsPath stringByAppendingPathComponent: [NSString stringWithFormat:@"newimages%@.plist", manufacturerID]];
                [responseString writeToFile:fileName atomically:YES encoding:NSUTF8StringEncoding error:nil];
                NSArray *array = [[NSArray alloc] initWithContentsOfFile:fileName];

                for (NSDictionary* dict in array) {

                    NSString *fileName = [NSString stringWithFormat:@"%@/%@_tn.jpg?t=",manufacturerID, [[dict valueForKey:@"ItemID"]  stringByReplacingOccurrencesOfString:@" " withString:@"%20"]];
                    NSString *savePath = [documentsPath stringByAppendingPathComponent:fileName];
                    NSURL *url = [NSURL URLWithString: [[NSString stringWithFormat:kProductImagesURL, fileName]stringByAppendingString:lastImagesSyncDate]];

                    __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
                    [request setCompletionBlock:^{

                        int statusCode = [request responseStatusCode];
                        if (statusCode==200) {
                            NSData *responseData = [request responseData];
                            [responseData writeToFile:[savePath stringByReplacingOccurrencesOfString:@"%20" withString:@" "] atomically:YES];
                        }  

                    }];
                    [request setFailedBlock:^{
                        //  NSError *error = [request error];
                    }];
                    [request startAsynchronous];  
                }

                [array release];     
            }];
            [request setFailedBlock:^{
                //  NSError *error = [request error];
            }];
            [request startAsynchronous];
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_release(newImages); //this executes on main thread
        });
    });
}

我假设这里只创建了 1 个队列,所有这些调用都添加到其中,一次运行 1 个,直到它们完成。我尝试了几种变体,它使我的应用程序陷入停顿 - 它没有崩溃,但主线程正在爬行。

更新: 所以我终于明白,在我的队列中拥有所有这些异步 ASIHTTPRequest 正在创建大量线程,因此更新了我的代码,让队列将这段代码的长时间运行部分添加到队列中,并改用同步请求:

 for (NSDictionary* dict in array) {
                dispatch_async(newImages, ^{  
                    NSString *fileName = [NSString stringWithFormat:@"%@/%@_tn.jpg?t=",manufacturerID, [[dict valueForKey:@"ItemID"]  stringByReplacingOccurrencesOfString:@" " withString:@"%20"]];
                    NSString *savePath = [documentsPath stringByAppendingPathComponent:fileName];
                    NSURL *url = [NSURL URLWithString: [[NSString stringWithFormat:kProductImagesURL, fileName]stringByAppendingString:lastImagesSyncDate]];

                    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
                    [request startSynchronous];
                    NSError *error = [request error];
                    if (!error) {
                        int statusCode = [request responseStatusCode];
                        if (statusCode==200) {
                            NSData *responseData = [request responseData];
                            [responseData writeToFile:[savePath stringByReplacingOccurrencesOfString:@"%20" withString:@" "] atomically:YES];
                        }  
                    }
                });
}

现在我只需要弄清楚在哪里再次释放队列,我已经尝试了几种变体但没有成功。

最佳答案

首先,您应该将队列命名为 using reverse domain syntax ,例如“com.foo.myapp.backgroundwork”——这确保了唯一性和一致性。

其次,您正在立即尝试释放刚刚创建的队列(并且可能正在做一些工作)。不要那样做。而是在工作完成后释放队列。以下内容实际记录在 Concurrency programming guide 中,在“管理队列内存”下

dispatch_async(newImages, ^{
        [self getNewImages];
        dispatch_async(dispatch_get_main_queue(), ^{
          dispatch_release(newImages); //this executes on main thread
        });
    });

编辑: 在我自己重新阅读并发指南(我自己吃药)后,我了解到运行时实际上会清理一个在后台线程上达到 0 引用计数的 dispatch_queue,所以不应该阻塞您的用户界面。立即释放它仍然是一个坏主意,而是按照我演示的做法,您的任务在主线程上排队并进行清理。因此,事后看来,我认为您的 -getNewImages 方法可能会导致您的 UI 阻塞。您能否展示该方法的一些代码,以便我们排除这种可能性?

关于iphone - dispatch_queue_t 仍然阻塞主线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6497743/

相关文章:

ios - HTTP GET 请求在 iOS 模拟器中有效,但在 iPhone 设备上无效

ios - NSDictionary 作为参数的不一致行为

c - C标准库中的函数线程安全吗?

iphone - 以特定风格在 iPad 上播放视频

iphone - 如何将 Amazon S3 类与 Walrus S3 结合使用?

iphone - 隐藏 UIToolbar UIBarButtonItems

c# - 线程和 session 对 asp.net c# 中的 paypal 机制的影响应该是什么

iphone - UIAlertViewStylePlainTextInput 返回键委托(delegate)

iphone - iOS 崩溃 'NSInternalInconsistencyException',原因 : 'statement is still active' Core Data cache related?

multithreading - 无法进入临界区