Syl*_*ier 7 cocoa multithreading objective-c thread-safety
我正在Objective-C中编写一个下载管理器,它可以同时从多个段下载文件,以提高速度.文件的每个部分都在一个线程中下载.
起初,我想将每个段写在不同的文件中,并在下载结束时将所有文件放在一起.但由于种种原因,这不是一个好的解决方案.
所以,我正在寻找一种在特定位置写入文件并且能够处理多个线程的方法,因为在我的应用程序中,每个段都被下载到一个线程中.在Java中,我知道FileChannel
完美的诀窍,但我不知道Objective-C.
mvd*_*vds 10
到目前为止给出的答案有一些明显的缺点:
线程安全,高效,无锁的方法是使用内存映射,其工作方式如下:
open()
用于读/写的文件mmap()
它在记忆中的某个地方.该文件现在"存在"内存中.munmap()
内存和close()
文件实际写入由内核处理 - 您的程序永远不会发出write
任何形式的系统调用.内存映射通常没什么缺点,并且广泛用于共享库之类的东西.
更新:一段代码说超过1000字......这是mmap
Mecki基于锁的多线程文件编写器的版本.请注意,写入简化为简单memcpy
,不能失败(!!),因此无需BOOL success
检查.性能等同于基于锁的版本.(通过并行写入100个1mb块进行测试)
关于对mmap
基于方法的"过度杀伤"的评论:这使用较少的代码行,不需要锁定,不太可能在写入时阻止,不需要在写入时检查返回值.唯一的"过度杀伤"是它需要开发人员理解另一个概念而不是良好的旧读/写文件I/O.
省略了直接读入mmapped内存区域的可能性,但实现起来非常简单.您可以直接read(fd,i_filedata+offset,length);
或recv(socket,i_filedata+offset,length,flags);
直接进入该文件.
@interface MultiThreadFileWriterMMap : NSObject
{
@private
FILE * i_outputFile;
NSUInteger i_length;
unsigned char *i_filedata;
}
- (id)initWithOutputPath:(NSString *)aFilePath length:(NSUInteger)length;
- (void)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset;
- (void)writeData:(NSData *)data toFileOffset:(off_t)offset;
- (void)close;
@end
#import "MultiThreadFileWriterMMap.h"
#import <sys/mman.h>
#import <sys/types.h>
@implementation MultiThreadFileWriterMMap
- (id)initWithOutputPath:(NSString *)aFilePath length:(NSUInteger)length
{
self = [super init];
if (self) {
i_outputFile = fopen([aFilePath UTF8String], "w+");
i_length = length;
if ( i_outputFile ) {
ftruncate(fileno(i_outputFile), i_length);
i_filedata = mmap(NULL,i_length,PROT_WRITE,MAP_SHARED,fileno(i_outputFile),0);
if ( i_filedata == MAP_FAILED ) perror("mmap");
}
if ( !i_outputFile || i_filedata==MAP_FAILED ) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc
{
[self close];
[super dealloc];
}
- (void)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset
{
memcpy(i_filedata+offset,bytes,length);
}
- (void)writeData:(NSData *)data toFileOffset:(off_t)offset
{
memcpy(i_filedata+offset,[data bytes],[data length]);
}
- (void)close
{
munmap(i_filedata,i_length);
i_filedata = NULL;
fclose(i_outputFile);
i_outputFile = NULL;
}
@end
Run Code Online (Sandbox Code Playgroud)
永远不要忘记,Obj-C 基于普通 C,因此我只需编写一个自己的类,它使用标准 C API 处理文件 I/O,它允许您将当前写入位置放置在新文件中的任何位置,甚至远远超出当前文件大小(缺少的字节用零字节填充),以及根据需要向前和向后跳跃。实现线程安全的最简单方法是使用锁,这不一定是最快的方法,但在您的具体情况下,我敢打赌瓶颈肯定不是线程同步。该类可以有这样的标题:
@interface MultiThreadFileWriter : NSObject
{
@private
FILE * i_outputFile;
NSLock * i_fileLock;
}
- (id)initWithOutputPath:(NSString *)aFilePath;
- (BOOL)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset;
- (BOOL)writeData:(NSData *)data toFileOffset:(off_t)offset;
- (void)close;
@end
Run Code Online (Sandbox Code Playgroud)
以及与此类似的实现:
#import "MultiThreadFileWriter.h"
@implementation MultiThreadFileWriter
- (id)initWithOutputPath:(NSString *)aFilePath
{
self = [super init];
if (self) {
i_fileLock = [[NSLock alloc] init];
i_outputFile = fopen([aFilePath UTF8String], "w");
if (!i_outputFile || !i_fileLock) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc
{
[self close];
[i_fileLock release];
[super dealloc];
}
- (BOOL)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset
{
BOOL success;
[i_fileLock lock];
success = i_outputFile != NULL
&& fseeko(i_outputFile, offset, SEEK_SET) == 0
&& fwrite(bytes, length, 1, i_outputFile) == 1;
[i_fileLock unlock];
return success;
}
- (BOOL)writeData:(NSData *)data toFileOffset:(off_t)offset
{
return [self writeBytes:[data bytes] ofLength:[data length]
toFileOffset:offset
];
}
- (void)close
{
[i_fileLock lock];
if (i_outputFile) {
fclose(i_outputFile);
i_outputFile = NULL;
}
[i_fileLock unlock];
}
@end
Run Code Online (Sandbox Code Playgroud)
可以通过多种方式避免锁定。使用 Grand Central Dispatch 和 Blocks 来安排串行队列上的查找 + 写入操作是可行的。另一种方法是使用 UNIX (POSIX) 文件处理程序而不是标准 C 文件处理程序(open()
用 andint
代替FILE *
and fopen()
),多次复制处理程序(dup()
函数),然后将每个处理程序放置到不同的文件偏移量,这避免了进一步查找操作每次写入和锁定,因为 POSIX I/O 是线程安全的。然而,这两种实现都会稍微复杂一些,可移植性较差,并且不会有可测量的速度改进。
归档时间: |
|
查看次数: |
3388 次 |
最近记录: |