Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/107.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
ios基于密钥创建写锁_Ios_Multithreading_Asynchronous_Grand Central Dispatch_Nslock - Fatal编程技术网

ios基于密钥创建写锁

ios基于密钥创建写锁,ios,multithreading,asynchronous,grand-central-dispatch,nslock,Ios,Multithreading,Asynchronous,Grand Central Dispatch,Nslock,我知道如何使用dispatch\u barrier\u async锁定给定的资源,但在我的例子中,它不是一个好的选择,因为我没有修改共享数据结构,而是磁盘上的资源,并且不想阻塞整个队列,而只是一个给定的密钥,因为操作可能需要很长时间。我不确定文件系统如何从多个线程同时访问同一个文件(按名称),在文档中找不到明确的答案,只有最佳实践。我想我想按“文件名”锁定,但缺少一个方法“tryLock(key)” 比如: -(void)readFileAtPath:(NSString *)path compl

我知道如何使用
dispatch\u barrier\u async
锁定给定的资源,但在我的例子中,它不是一个好的选择,因为我没有修改共享数据结构,而是磁盘上的资源,并且不想阻塞整个队列,而只是一个给定的密钥,因为操作可能需要很长时间。我不确定文件系统如何从多个线程同时访问同一个文件(按名称),在文档中找不到明确的答案,只有最佳实践。我想我想按“文件名”锁定,但缺少一个方法“
tryLock(key)

比如:

-(void)readFileAtPath:(NSString *)path completion:(void(^)(NSData *fileData))completion
{
   dispatch_async(self.concurrentQueue,^{
       // acquire the lock for a given key and block until can acquire
       trylock(path);
       NSData *fileData = [self dataAtPath:path];
       unlock(path);
       completion(fileData);
   });
}

-(void)writeData:(NSData *)data toPath:(NSString *)path completion:(void(^)())completion
{
    dispatch_async(self.concurrentQueue,^{
        // if someone is reading the data at 'path' then this should wait - otherwise should write
        trylock(path);
        [data writeToFile:path atomically:YES];
        unlock(path);
        completion();
    });
}
编辑:

@synchronized
是否执行此操作?这是一个合适的用例吗?

如果您想创建“作用域队列”,只需这样做。为每个文件创建一个串行队列,并使它们以并发队列为目标。可能是这样的:

@interface Foo : NSObject
@property (readonly) dispatch_queue_t concurrentQueue;
@end

@implementation Foo
{
    NSMutableDictionary* _fileQueues;
    dispatch_queue_t _dictGuard;
}

@synthesize concurrentQueue = _concurrentQueue;

- (instancetype)init
{
    if (self = [super init])
    {
        _concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        _dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
        _fileQueues = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (dispatch_queue_t)queueForFile: (NSString*)path
{
    __block dispatch_queue_t retVal = NULL;
    dispatch_sync(_dictGuard, ^{
        retVal = _fileQueues[path];
        if (!retVal)
        {
            retVal = dispatch_queue_create(path.UTF8String, DISPATCH_QUEUE_SERIAL);
            dispatch_set_target_queue(retVal, self.concurrentQueue);
            _fileQueues[path] = retVal;
        }
    });
    return retVal;
}

- (void)doStuff: (id)stuff withFile: (NSString*)path
{
    dispatch_queue_t fileQueue = [self queueForFile: path];
    dispatch_async(fileQueue, ^{
        DoStuff(stuff, path);
    });
}

@end
@interface Bar : NSObject
@property (readonly) dispatch_queue_t concurrentQueue;
@end

@implementation Bar
{
    NSMutableDictionary* _lockObjects;
    dispatch_queue_t _dictGuard;
}

- (instancetype)init
{
    if (self = [super init])
    {
        _concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        _dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
        _lockObjects = [[NSMutableDictionary alloc] init];
    }
    return self;
}

@synthesize concurrentQueue = _concurrentQueue;

- (id)lockForFile: (NSString*)path
{
    __block id retVal = NULL;
    dispatch_sync(_dictGuard, ^{
        retVal = _lockObjects[path];
        if (!retVal)
        {
            retVal = [[NSObject alloc] init];
            _lockObjects[path] = retVal;
        }
    });
    return retVal;
}

- (void)syncDoStuff: (id)stuff withFile: (NSString*)path
{
    id fileLock = [self lockForFile: path];
    @synchronized(fileLock)
    {
        DoStuff(stuff, path);
    }
}

- (void)asyncDoStuff: (id)stuff withFile: (NSString*)path
{
    id fileLock = [self lockForFile: path];
    dispatch_async(self.concurrentQueue, ^{
        @synchronized(fileLock)
        {
            DoStuff(stuff, path);
        }
    });
}

@end
这就是说,每个文件队列有一点“代码味道”,特别是如果它是为了提高I/O性能的话。为了获得最高性能,我觉得每个物理设备都有一个队列比每个文件都有一个队列更好。通常情况下,作为开发人员,您并不比操作系统/系统框架更了解如何协调文件系统访问,因此您肯定希望在访问之前和之后进行测量,以确保这种方法实际上提高了您的性能。当然,有时你会知道一些操作系统不知道的事情,但是你可能想寻找一种方法给操作系统提供这些信息,而不是重新发明轮子。在读写性能方面,如果您使用
dispatch_io
通道来读写文件,您将向GCD提供其所需的信息,以最好地协调您的文件访问

我还想到,您可能还试图“保护应用程序不受自身影响”。例如,如果您将磁盘用作缓存,其中多个任务可能同时访问文件,则可能需要保护读卡器不受其他写入程序的影响。如果是这种情况,您可能需要寻找一些现有的框架,这些框架可能比您自己的框架更好地满足需求。此外,在这个用例中,您可能需要考虑在应用程序中管理范围,而只是代码> MMAP>代码>一个大文件,但是这种方法的成本/收益将取决于文件的粒度大小。 如果没有更多关于应用程序的上下文,就很难说得更多

对于您接下来的问题:
@synchronized
可以用来实现这一点,但是没有与上面发布的GCD方式相同的机制。这是因为
@synchronized(foo)
通过标识(指针相等)而不是值相等(即
-isEqual:
)在
foo
上同步,因此
NSString
NSURL
(用于引用文件的两个最明显的对象)具有值语义,这两个对象很难匹配。使用
@synchronized
的实现可能如下所示:

@interface Foo : NSObject
@property (readonly) dispatch_queue_t concurrentQueue;
@end

@implementation Foo
{
    NSMutableDictionary* _fileQueues;
    dispatch_queue_t _dictGuard;
}

@synthesize concurrentQueue = _concurrentQueue;

- (instancetype)init
{
    if (self = [super init])
    {
        _concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        _dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
        _fileQueues = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (dispatch_queue_t)queueForFile: (NSString*)path
{
    __block dispatch_queue_t retVal = NULL;
    dispatch_sync(_dictGuard, ^{
        retVal = _fileQueues[path];
        if (!retVal)
        {
            retVal = dispatch_queue_create(path.UTF8String, DISPATCH_QUEUE_SERIAL);
            dispatch_set_target_queue(retVal, self.concurrentQueue);
            _fileQueues[path] = retVal;
        }
    });
    return retVal;
}

- (void)doStuff: (id)stuff withFile: (NSString*)path
{
    dispatch_queue_t fileQueue = [self queueForFile: path];
    dispatch_async(fileQueue, ^{
        DoStuff(stuff, path);
    });
}

@end
@interface Bar : NSObject
@property (readonly) dispatch_queue_t concurrentQueue;
@end

@implementation Bar
{
    NSMutableDictionary* _lockObjects;
    dispatch_queue_t _dictGuard;
}

- (instancetype)init
{
    if (self = [super init])
    {
        _concurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
        _dictGuard = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
        _lockObjects = [[NSMutableDictionary alloc] init];
    }
    return self;
}

@synthesize concurrentQueue = _concurrentQueue;

- (id)lockForFile: (NSString*)path
{
    __block id retVal = NULL;
    dispatch_sync(_dictGuard, ^{
        retVal = _lockObjects[path];
        if (!retVal)
        {
            retVal = [[NSObject alloc] init];
            _lockObjects[path] = retVal;
        }
    });
    return retVal;
}

- (void)syncDoStuff: (id)stuff withFile: (NSString*)path
{
    id fileLock = [self lockForFile: path];
    @synchronized(fileLock)
    {
        DoStuff(stuff, path);
    }
}

- (void)asyncDoStuff: (id)stuff withFile: (NSString*)path
{
    id fileLock = [self lockForFile: path];
    dispatch_async(self.concurrentQueue, ^{
        @synchronized(fileLock)
        {
            DoStuff(stuff, path);
        }
    });
}

@end

你会看到我用了两种方法来做事情,一种是同步的,另一种是异步的
@synchronized
提供了一种互斥机制,但不是一种异步调度机制,因此如果您想要并行,您仍然必须从GCD(或其他东西)获得并行性。它的长与短之处在于,尽管您可以使用
@synchronized
来实现这一点,但现在它不是一个好的选择。它比同等的GCD机制慢得多。现在,
@synchronized
唯一有用的时间是作为实现递归锁定的语法快捷方式。也就是说,许多聪明人认为递归锁定是一种反模式。(有关原因的更多详细信息,请查看。)长话短说,
@synchronized
不是解决此问题的最佳方法。

太棒了!将进行各种方式的测试