我有一个使用Grand Central调度队列的工作实现:(1)打开文件并在“queue1”上计算OpenSSL DSA哈希,(2)将哈希写出到新的“侧车”文件以便以后验证“队列2”。
我想同时打开多个文件,但是基于一些不会通过打开100个文件并超过硬盘驱动器可持续输出而“阻塞”操作系统的逻辑。照片浏览应用程序(如iPhoto或Aperture)似乎打开多个文件并显示它们,所以我假设可以这样做。
我假设最大的限制是磁盘I / O,因为应用程序可以(理论上)同时读写多个文件。
有什么建议吗?
TIA
答案 0 :(得分:7)
你是正确的,因为你肯定会受到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对象。
无论如何,上述情况将同时处理三件事,无论有多少人入队。
答案 1 :(得分:6)
我使用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允许您独立定义工作单元和它们之间的依赖关系。在某些情况下,这可以更容易理解和调试。
答案 2 :(得分:6)
你已经收到了很好的答案,但我想补充几点。我曾参与枚举文件系统中所有文件的项目,并计算每个文件的MD5和SHA1哈希值(除了其他处理)。如果您正在做类似的事情,在那里搜索大量文件并且文件可能包含任意内容,那么需要考虑以下几点:
如上所述,您将受到I / O限制。如果同时读取多个文件,则会对每个计算的性能产生负面影响。显然,并行调度计算的目标是使磁盘在文件之间保持忙碌,但您可能需要考虑以不同方式构建工作。例如,设置一个枚举并打开文件的线程,第二个线程从第一个线程一次获取打开文件句柄并处理它们。文件系统将缓存目录信息,因此枚举不会对读取数据产生严重影响,这实际上必须击中磁盘。
如果文件可能是任意大的,那么Chris的方法可能不实用,因为整个内容都被读入内存。
如果您对数据没有其他用途而不是计算哈希值,那么我建议在读取数据之前禁用文件系统缓存。
如果使用NSFileHandles,一个简单的类别方法将按文件执行此操作:
@interface NSFileHandle (NSFileHandleCaching)
- (BOOL)disableFileSystemCache;
@end
#include <fcntl.h>
@implementation NSFileHandle (NSFileHandleCaching)
- (BOOL)disableFileSystemCache {
return (fcntl([self fileDescriptor], F_NOCACHE, 1) != -1);
}
@end
如果边车文件很小,您可能希望将它们收集在内存中并分批写出来,以尽量减少处理中断。
文件系统(至少HFS)按顺序存储文件在目录中的文件记录,因此遍历文件系统广度优先(即,在进入子目录之前处理目录中的每个文件)。
以上只是建议,当然。您需要尝试并测量性能以确认实际影响。
答案 3 :(得分:2)
libdispatch实际上为此明确提供了API!查看dispatch_io;它将在适当的时候处理并行化IO,否则将其序列化以避免颠簸磁盘。
答案 4 :(得分:1)
以下链接指向我使用NSOperation和Grand Central Dispatch设置的BitBucket项目,使用原始文件完整性应用程序。
https://bitbucket.org/torresj/hashar-cocoa
我希望它有所帮助/使用。