ios在后台线程上写入磁盘

时间:2013-08-19 02:21:35

标签: ios multithreading

我目前正在通过调用

在后台线程中将一些文件写入磁盘
dispatch_async(my_queue,^{
   [self writeToRoot:filename data:data];
};

- (BOOL)writeToRoot:(NSString *)path data:(NSData *)content
{
    NSString *fullPath = [[self rootPath] stringByAppendingPathComponent:path];

    NSString *pathWithoutFile = [fullPath stringByDeletingLastPathComponent];

    BOOL directoryExists = [[NSFileManager defaultManager] fileExistsAtPath:pathWithoutFile];

    if (!directoryExists) {
        NSError *error = nil;
        [[NSFileManager defaultManager] createDirectoryAtPath:pathWithoutFile
                                  withIntermediateDirectories:YES
                                                   attributes:nil error:&error];
        NSParameterAssert(error == nil);
    }

    return [content writeToFile:fullPath atomically:NO];
}

我这样做是因为它不会阻止主线程。我的问题是如何确保线程安全。在执行此后台操作时,当我尝试通过调用:

从磁盘读取文件时会发生什么
[NSData dataWithContentsOfFile:fullPath];

内容会被破坏吗? 或者写操作会锁定文件,读操作会等到写完成吗?

2 个答案:

答案 0 :(得分:20)

我倾向于dispatch_syncmy_queue进行读操作以确保线程安全(假设它是一个串行队列)。您也可以使用各种synchronization tools中的任何一个(例如lock或@synchronized指令),但鉴于您已经为文件交互设置了队列,使用该串行队列可能是最简单的。

并发编程指南的Eliminating Lock-Based Code部分讨论了使用队列协调与共享资源交互的技术。


顺便说一句,如果你在后台队列中保存(这意味着保存操作可能足够慢以证明在后台进行操作),那么确保你请求一点时间来完成它可能是明智的。在保存操作正在进行时,应用程序本身被中断(即用户点击物理主页按钮,呼叫进入等)的操作。您可以在分派保存操作之前调用beginBackgroundTaskWithExpirationHandler,并在完成后调用endBackgroundTask

UIApplication *application = [UIApplication sharedApplication];

// get background task identifier before you dispatch the save operation to the background

UIBackgroundTaskIdentifier __block task = [application beginBackgroundTaskWithExpirationHandler:^{
    if (task != UIBackgroundTaskInvalid) {
        [application endBackgroundTask:task];
        task = UIBackgroundTaskInvalid;
    }
}];

// now dispatch the save operation

dispatch_async(my_queue, ^{

    // do the save operation here

    // now tell the OS that you're done

    if (task != UIBackgroundTaskInvalid) {
        [application endBackgroundTask:task];
        task = UIBackgroundTaskInvalid;
    }
});

即使应用程序被中断,这也将确保您的保存操作有成功完成的机会。

而且,正如Jsdodgers指出的那样,你可能也想要执行原子写法。

答案 1 :(得分:2)

由于您的代码现在正确,是的,会出现问题。这是因为您将其设置为不以原子方式传输:

return [content writeToFile:fullPath atomically:NO];

原子意味着不是删除文件然后开始写入,而是将文件写入单独的临时文件位置。一旦文件完全写入,它就会删除旧版本的文件(如果存在)并将新文件重命名为正确的名称。如果传输未完成,则不会发生任何事情,只应删除临时文件。

因此,如果您在该行中原子地更改为YES,则调用该数据将返回旧数据,直到保存完成,之后随时将获取新数据。

为此,您需要:

return [content writeToFile:fullPath atomically:YES];