简体   繁体   English

无法对文件执行GCD I / O

[英]can't perform GCD i/o for files

I need to load and write images asynchronously - but i can't access the file if it is writing now. 我需要异步加载和写入图像-但如果正在写入,则无法访问该文件。 For this purposes I want to use barrier_async for write and sync for read files. 为此,我想使用barrier_async进行写入,并使用sync进行读取文件。 Here is my code: 这是我的代码:

The part of the block method to perform gcd operations: 执行gcd操作的block方法的一部分:

    [NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:
     ^(NSURLResponse *response, NSData *data, NSError *connectionError)
     {   
         [self.class writeData:data toFilePath:tileFilePathName completionHandler:^(NSError *error) {
             if (!error) {
                 dispatch_sync(dispatch_get_main_queue(), ^{
                     [[AxPanoramaDataManager sharedInstance].tilesNamesArray addObject:tileFileName];
                 });
                 [self.class readImagefromFilePath:tileFilePathName
                                 completionHandler:^(UIImage *image, NSError *error) {
                                     if (!error)
                                         dispatch_sync(dispatch_get_main_queue(), ^{
                                             completion(tileCoordValue, side, image, error);
                                         });
                                 }];
             }
         }];
     }];

and read/write methods: 和读/写方法:

+ (void) readImagefromFilePath: (NSString *) filePath
             completionHandler:(void (^)(UIImage* image, NSError* error)) handler
{
    dispatch_sync([AxPanoramaDataManager sharedInstance].dataManagerQueue, ^{
        UIImage *tileImage = [UIImage imageWithContentsOfFile:filePath];
        dispatch_sync(dispatch_get_main_queue(), ^{
            handler(tileImage, nil);
            NSLog(@"Image %@ loaded from cash", tileImage);
        });
    });
}

+ (void) writeData: (NSData *) data
        toFilePath: (NSString *) filePath
 completionHandler:(void (^)(NSError* error)) handler
{
    dispatch_barrier_async([AxPanoramaDataManager sharedInstance].dataManagerQueue, ^{
        [data writeToFile:filePath atomically:YES];
        dispatch_sync(dispatch_get_main_queue(), ^{
            handler(nil);
            NSLog(@"Data %@ wrote to the disk", data);
        });
    });
}

Now the app is hanging while I try to perform this method. 现在,当我尝试执行此方法时,该应用程序已挂起。 Any help? 有什么帮助吗?

You're deadlocking yourself. 您陷入僵局。 Here's that code "unrolled" to be a single call. 这是将代码“展开”为一次调用的代码。 (I'll break it apart below.) (我在下面将其分解。)

[NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler: ^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    // Unroll: [self.class writeData: data toFilePath: filePath completionHandler: writeDataCompletion];
    dispatch_barrier_async(dataManagerQueue, ^{
        [data writeToFile:filePath atomically:YES];
        dispatch_sync(dispatch_get_main_queue(), ^{
            // Unroll: writeDataCompletion(nil);
            NSError* error = nil;
            if (!error) {
                dispatch_sync(dispatch_get_main_queue(), ^{
                    [[AxPanoramaDataManager sharedInstance].tilesNamesArray addObject:tileFileName];
                });

                // Unroll: [self.class readImagefromFilePath:tileFilePathName completionHandler:readCompletion];
                dispatch_sync(dataManagerQueue, ^{
                    UIImage *tileImage = [UIImage imageWithContentsOfFile:filePath];
                    dispatch_sync(dispatch_get_main_queue(), ^{
                        // Unroll: readCompletion(tileImage, nil);
                        NSError* error = nil;
                        if (!error) {
                            dispatch_sync(dispatch_get_main_queue(), ^{
                                completion(tileCoordValue, side, tileImage, error);
                            });
                        }
                        NSLog(@"Image %@ loaded from cash", tileImage);
                    });
                });
            }
            NSLog(@"Data %@ wrote to the disk", data);
        });
    });
}];

Now let's trace through it line by line, noting what thread/queue we're on at each phase: 现在,让我们逐行进行跟踪,注意每个阶段我们所处的线程/队列:

[NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler: ^(NSURLResponse *response, NSData *data, NSError *connectionError) {

OK, so when you get into the callback from -[NSURLConnection sendAsync...] you're on the main thread, because you passed [NSOperationQueue mainQueue] to the queue: parameter. 好的,因此,当您从-[NSURLConnection sendAsync...]进入回调时,您将进入主线程,因为您[NSOperationQueue mainQueue]传递给queue:参数。

    // Unroll: [self.class writeData: data toFilePath: filePath completionHandler: writeDataCompletion];
    dispatch_barrier_async(dataManagerQueue, ^{

Now we're on dataManagerQueue , in a barrier block, which means that until we return from this block, nothing else can run on dataManagerQueue . 现在我们在障碍块中的dataManagerQueue ,这意味着除非我们从该块返回,否则dataManagerQueue上不能运行任何其他dataManagerQueue Because the barrier call is async, we expect the main thread/queue to be free at this point. 因为屏障调用是异步的,所以我们希望此时主线程/队列是空闲的。

        [data writeToFile:filePath atomically:YES];
        dispatch_sync(dispatch_get_main_queue(), ^{

Now we're back on the main queue. 现在我们回到了主队列。 Note that because this was called with dispatch_sync we are also still in the barrier block on dataManagerQueue . 请注意,因为这是一个名为dispatch_sync我们仍然在隔离块dataManagerQueue

            // Unroll: writeDataCompletion(nil);
            NSError* error = nil;
            if (!error) {
                dispatch_sync(dispatch_get_main_queue(), ^{

We were already on the main queue, so dispatch_sync(dispatch_get_main_queue() is going to deadlock here. We're dead in the water at this point, but let's keep going anyway, and assume for the moment that dispatch_sync handles recursive re-entry (it doesn't, but...) 我们已经在主队列中了,所以dispatch_sync(dispatch_get_main_queue()将会在此处陷入死锁。这时我们已经死在水里了,但是无论如何,让我们继续前进,并假设一下dispatch_sync处理递归重入(它没有,但是...)

                    [[AxPanoramaDataManager sharedInstance].tilesNamesArray addObject:tileFileName];
                });

                // Unroll: [self.class readImagefromFilePath:tileFilePathName completionHandler:readCompletion];
                dispatch_sync(dataManagerQueue, ^{

Now, notice that we're still in the barrier block that you submitted to dataManagerQueue but we're attempting to submit another block via dispatch_sync(dataManagerQueue, ...) . 现在,请注意,我们仍处在您提交给dataManagerQueue的障碍块中,但是我们正尝试通过dispatch_sync(dataManagerQueue, ...)提交另一个块。 So if we weren't already deadlocked on the main queue above, now we would be deadlocked on dataManagerQueue . 因此,如果我们尚未在上面的主队列上陷入僵局,那么现在我们将在dataManagerQueuedataManagerQueue

                    UIImage *tileImage = [UIImage imageWithContentsOfFile:filePath];
                    dispatch_sync(dispatch_get_main_queue(), ^{

And now we're synchronously re-entering the main queue again ! 而现在我们正在同步重新进入再次的主队列!

                        // Unroll: readCompletion(tileImage, nil);
                        NSError* error = nil;
                        if (!error) {
                            dispatch_sync(dispatch_get_main_queue(), ^{

And again !!! 再来一次

                                completion(tileCoordValue, side, tileImage, error);
                            });
                        }
                        NSLog(@"Image %@ loaded from cash", tileImage);
                    });
                });
            }
            NSLog(@"Data %@ wrote to the disk", data);
        });
    });
}];

In short, you have numerous deadlocks here. 简而言之,这里有许多僵局。 You seem to be using dispatch_sync in many places where you could be using dispatch_async but I can't possibly know what else is in play that made you think all these completions need to be fired synchronously. 您似乎在许多可以使用dispatch_sync的地方都使用了dispatch_async但是我可能不知道还有什么其他原因使您认为所有这些完成都需要同步触发。 Based on the code you posted, you could start by turning every _sync call into an _async call with no substantive ill effects (ie the only effect visible from the code posted here would be that the NSLog s will fire at different times.) 根据您发布的代码,您可以将每个_sync调用转换为一个_async调用,而不会产生实质性的不良影响(即,从此处发布的代码中看到的唯一影响是NSLog会在不同时间触发)。

Another general rule of thumb is that dispatch_sync(dispatch_get_main_queue(), ...) is almost always a bad idea. 另一个一般经验法则是dispatch_sync(dispatch_get_main_queue(), ...)几乎总是一个坏主意。 (See detailed explanation ) Even if it works "most of the time" it is problematic, because things out of your control (ie in the OS) could interact with that pattern in a way that can cause a deadlock. (请参阅详细说明 )即使它“大部分时间”都有效,也是有问题的,因为无法控制的事情(例如,在OS中)可能与该模式交互,从而导致死锁。 You probably want to rework anywhere you rely on a dispatch_sync(dispatch_get_main_queue(), ...) to be a dispatch_async(dispatch_get_main_queue(), ... ) followed by a nested re-dispatching of the remaining background work to a background queue. 您可能想在依赖dispatch_sync(dispatch_get_main_queue(), ...)任何地方进行重做dispatch_sync(dispatch_get_main_queue(), ...)使其成为dispatch_async(dispatch_get_main_queue(), ... )然后将剩余的后台工作嵌套重新分派到后台队列。 But generally speaking, most main thread completions should be able to be dispatched asynchronously without an issue. 但是一般来讲,大多数主线程完成应该能够异步发送而没有问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM