Writing in a file from multiple threads

前端 未结 3 1335
挽巷
挽巷 2021-02-04 17:10

I\'m writing a download manager in Objective-C which downloads file from multiple segments at the same times in order to improve the speed. Each segement of the file is download

3条回答
  •  误落风尘
    2021-02-04 18:04

    Never forget, Obj-C bases on normal C and thus I would just write an own class, that handles file I/O using standard C API, which allows you to place the current write position anywhere within a new file, even far beyond the current file size (missing bytes are filled with zero bytes), as well as jumping forward and backward as you wish. The easiest way to achieve thread-safety is using a lock, this is not necessary the fastest way but in your specific case, I bet that the bottleneck is certainly not thread-synchronization. The class could have a header like this:

    @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
    

    And an implementation similar to this one:

    #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
    

    The lock could be avoided in various way. Using Grand Central Dispatch and Blocks to schedule the seek + write operations on a Serial Queue would work. Another way would be to use UNIX (POSIX) file handlers instead of standard C ones (open() and int instead of FILE * and fopen()), duplicate the handler multiple times (dup() function) and then placing each of them to a different file offset, which avoids further seeking operations on each write and also locking, since POSIX I/O is thread-safe. However, both implementations would be somewhat more complicating, less portable and there would be no measurable speed improvement.

提交回复
热议问题