简体   繁体   中英

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. Here is my code:

The part of the block method to perform gcd operations:

    [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.

    // 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 . 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 .

            // 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...)

                    [[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, ...) . So if we weren't already deadlocked on the main queue above, now we would be deadlocked on dataManagerQueue .

                    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. 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.)

Another general rule of thumb is that dispatch_sync(dispatch_get_main_queue(), ...) is almost always a bad idea. (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. 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. But generally speaking, most main thread completions should be able to be dispatched asynchronously without an issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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