dispatch_queue_t仍然阻塞主线程

Sle*_*lee 7 iphone multithreading objective-c ios

我想启动一个方法并让它在后台运行 - 我不关心它启动后它真正发生了什么.

所以在我的主viewDidLoadMethod我有我所有的通常代码和这个:

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

我的假设是队列将被创建,然后该函数调用将设置为在后台线程中运行,我的应用程序将继续巡航.情况似乎并非如此.

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

编辑 - 阻止的代码:

-(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
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

我假设这里只创建了一个队列,所有这些调用都添加到它一次运行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];
                        }  
                    }
                });
}
Run Code Online (Sandbox Code Playgroud)

现在我只需要找出再次释放队列的位置,我已经尝试了几个没有运气的变化.

Rya*_*anR 5

首先,您应该使用反向域语法命名队列,例如"com.foo.myapp.backgroundwork" - 这可以确保唯一性和一致性.

其次,您正在尝试释放刚刚创建的队列(并且可能正在做一些工作).不要那样做.改为完成工作后释放队列.以下内容实际上记录在" 并发编程指南 "的"管理队列内存"下

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

编辑:在我自己重新阅读并发指南(我自己吃药)之后,我了解到运行时实际上会清理一个在后台线程上达到0引用计数的dispatch_queue,因此不应该阻止你的UI.立即发布它仍然是一个坏主意,而是遵循我演示的实践,你的任务在主线程上清理它的清理.所以,事后我认为你的-getNewImages方法可能会阻止你的UI.你能从这种方法中显示一些代码,以便我们可以排除这种情况吗?


Sle*_*lee 3

我绕了很长的路,但最终在我的脑海中得到了这个概念,这对我来说是完美的:

dispatch_queue_t newImages = dispatch_queue_create("com.mydomain.app.newimagesinbackground", NULL); // create my serial queue
        dispatch_async(newImages, ^{
     [self getNewImages]; // call my function - this get added first in my serial queue
    });


    dispatch_async(newImages, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            // add this to the main queue as the last item in my serial queue
            // when I get to this point I know everything in my queue has been run
            dispatch_release(newImages);
        });
    });
Run Code Online (Sandbox Code Playgroud)

我的主要问题是使用 ASIHTTPRequest startAsynchronous 方法: http://allseeing-i.com/ASIHTTPRequest/How-to-use#using_blocks

这本质上是造成我的瓶颈,需要获取 2000 个图像,尝试进行 2000 个异步调用。如果我已经在后台,startSynchronous 方法就可以正常工作,并且一次只有 1 个调用尝试运行。