打开多个档案的大中央战略
我有一个使用Grand Central调度队列的工作实现:(1)打开文件并在“queue1”上计算OpenSSL DSA哈希,(2)将哈希写出到新的“side car”文件,以便稍后validation“queue2” 。
我想同时打开多个文件,但是基于一些不会通过打开100个文件并超过硬盘驱动器可持续输出而“阻塞”操作系统的逻辑。 照片浏览应用程序(如iPhoto或Aperture)似乎打开多个文件并显示它们,所以我假设可以这样做。
我假设最大的限制是磁盘I / O,因为应用程序可以(理论上)同时读写多个文件。
有什么建议?
TIA
你是正确的,因为你肯定会受到I / O限制。 而且,随着多个文件打开并同时被主动阅读的随机访问性质,它将更加复杂。
因此,你需要取得一些平衡。 正如您所观察到的那样,更有可能的是,一个文件并不是最有效的文件。
亲身?
我会使用调度信号量。
就像是:
@property(nonatomic, assign) dispatch_queue_t dataQueue; @property(nonatomic, assign) dispatch_semaphore_t execSemaphore;
和:
- (void) process:(NSData *)d { dispatch_async(self.dataQueue, ^{ if (!dispatch_semaphore_wait(self.execSemaphore, DISPATCH_TIME_FOREVER)) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ ... do calcualtion work here on d ... dispatch_async(dispatch_get_main_queue(), ^{ .... update main thread w/new data here .... }); dispatch_semaphore_signal(self.execSemaphore); }); } }); }
在哪里开始:
self.dataQueue = dispatch_queue_create("com.yourcompany.dataqueue", NULL); self.execSemaphore = dispatch_semaphore_create(3); [self process: ...]; [self process: ...]; [self process: ...]; [self process: ...]; [self process: ...]; .... etc ....
您需要确定如何最好地处理排队。 如果有很多项目并且有取消的概念,那么将所有内容排入队列可能会造成浪费。 同样,您可能希望将URL排入要处理的文件,而不是像上面那样的NSData对象。
无论如何,上述情况将同时处理三件事,无论有多少人入队。
我使用NSOperation是因为它易于处理依赖性和取消。
我创建了一个操作,分别用于读取数据文件,计算数据文件的哈希值以及编写sidecar文件。 我将使每个写操作依赖于其关联的计算操作,并且每个计算操作取决于其相关的读操作。
然后我将读取和写入操作添加到一个NSOperationQueue,即“I / O队列”,其宽度有限。 我将计算操作添加到单独的NSOperationQueue,即“计算队列”,其宽度不受限制。
I / O队列宽度受限的原因是您的工作可能受I / O限制; 您可能希望它的宽度大于1,但很可能与输入文件所在的物理磁盘数量直接相关。 (可能类似于2x,你需要通过实验来确定。)
代码最终会看起来像这样:
@implementation FileProcessor static NSOperationQueue *FileProcessorIOQueue = nil; static NSOperationQueue *FileProcessorComputeQueue = nil; + (void)inititalize { if (self == [FileProcessor class]) { FileProcessorIOQueue = [[NSOperationQueue alloc] init]; [FileProcessorIOQueue setName:@"FileProcessorIOQueue"]; [FileProcessorIOQueue setMaxConcurrentOperationCount:2]; // limit width FileProcessorComputeQueue = [[NSOperationQueue alloc] init]; [FileProcessorComputeQueue setName:@"FileProcessorComputeQueue"]; } } - (void)processFilesAtURLs:(NSArray *)URLs { for (NSURL *URL in URLs) { __block NSData *fileData = nil; // set by readOperation __block NSData *fileHashData = nil; // set by computeOperation // Create operations to do the work for this URL NSBlockOperation *readOperation = [NSBlockOperation blockOperationWithBlock:^{ fileData = CreateDataFromFileAtURL(URL); }]; NSBlockOperation *computeOperation = [NSBlockOperation blockOperationWithBlock:^{ fileHashData = CreateHashFromData(fileData); [fileData release]; // created in readOperation }]; NSBlockOperation *writeOperation = [NSBlockOperation blockOperationWithBlock:^{ WriteHashSidecarForFileAtURL(fileHashData, URL); [fileHashData release]; // created in computeOperation }]; // Set up dependencies between operations [computeOperation addDependency:readOperation]; [writeOperation addDependency:computeOperation]; // Add operations to appropriate queues [FileProcessorIOQueue addOperation:readOperation]; [FileProcessorComputeQueue addOperation:computeOperation]; [FileProcessorIOQueue addOperation:writeOperation]; } } @end
这很简单; 而不是像处理dispatch_*
API那样处理多重嵌套的同步/异步层,NSOperation允许您独立定义工作单元和它们之间的依赖关系。 在某些情况下,这可以更容易理解和调试。
你已经收到了很好的答案,但我想补充几点。 我曾参与枚举文件系统中所有文件的项目,并计算每个文件的MD5和SHA1哈希值(除了其他处理)。 如果您正在做类似的事情,在搜索大量文件并且文件可能包含任意内容的情况下,则需要考虑以下几点:
-
如上所述,您将受I / O限制。 如果同时读取多个文件,则会对每个计算的性能产生负面影响。 显然,并行调度计算的目标是使磁盘在文件之间保持忙碌,但您可能需要考虑以不同方式构建工作。 例如,设置一个枚举并打开文件的线程,第二个线程从第一个线程一次获取打开文件句柄并处理它们。 文件系统将缓存目录信息,因此枚举不会对读取数据产生严重影响,实际上必须触及磁盘。
-
如果文件可能是任意大的,那么Chris的方法可能不实用,因为整个内容被读入内存。
-
如果除了计算哈希值之外没有其他数据用途,那么我建议在读取数据之前禁用文件系统缓存。
如果使用NSFileHandles,一个简单的类别方法将为每个文件执行此操作:
@interface NSFileHandle (NSFileHandleCaching) - (BOOL)disableFileSystemCache; @end #include @implementation NSFileHandle (NSFileHandleCaching) - (BOOL)disableFileSystemCache { return (fcntl([self fileDescriptor], F_NOCACHE, 1) != -1); } @end
-
如果sidecar文件很小,您可能希望将它们收集在内存中并分批写出来以最大限度地减少处理中断。
-
文件系统(至少HFS)按顺序存储文件在目录中的文件记录,因此遍历文件系统广度优先(即,在进入子目录之前处理目录中的每个文件)。
当然,以上只是建议。 您需要尝试并测量性能以确认实际影响。
libdispatch实际上为此明确提供了API! 查看dispatch_io; 它将在适当时处理并行化IO,否则将其序列化以避免颠簸磁盘。
以下链接是我使用NSOperation和Grand Central Dispatch设置的BitBucket项目中使用的原始文件完整性应用程序。
https://bitbucket.org/torresj/hashar-cocoa
我希望它有帮助/使用。