如果方法已经在串行队列中执行,我是否需要使用锁?
Do I need to use locks if the methods are already executed in a serial queue?
我的库中公开了 2 个方法调用,如下所示:
-(void) createFile {
dispatch_async(serialQueue, ^{
[fileObj createFile:fileInfo completion:^(void){
//completion block C1
}];
});
}
-(void) readFile:(NSUInteger)timeStamp {
dispatch_async(serialQueue, ^{
[fileObj readFile:timeStamp completion:^(void){
//completion block C2
}];
});
}
现在 createFile:fileInfo:completion
和 readFile:timeStamp:completion
依次是调用进程 P1
的 XPC 调用。它们在进程 P1 中的实现如下所示:
@interface FileObject : NSObject
+ (instancetype) sharedInstance;
@property (nonatomic, strong) NSData *fileContents;
@end
@implementation FileObject
+ (instancetype)sharedInstance
{
static FileObject *sharedObj = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObj = [[self alloc] init];
});
return sharedObj;
}
- (void)createFile:(FileInfo *)fileInfo
completion:(void (^))completion {
FileObject *f = [FileObject sharedInstance];
//lock?
f.fileContents = //call some method;
//unlock
}
- (void)readFile:(NSUInteger)timeStamp
completion:(void (^))completion {
FileObject *f = [FileObject sharedInstance];
//do stuff with f.fileContents
}
@end
需要注意的一点是,对方法createFile
的调用能够对单例obj进行修改,并且readFile:fileInfo
总是在createFile
之后调用(使用串行队列调用时)。
我的问题是我的库中公开的两种方法的串行队列用法,
- 修改
readFileInfo:FileInfo:completion
中的f.fileContents
是否还需要加锁和解锁?
- 如果多个不同的进程调用我的库怎么办? XPC
P1
会自动处理还是我必须做些什么?
您问的是:
Do I need to use locks if the methods are already executed in a serial queue?
如果您可以同时从多个线程与非线程安全对象进行交互,是的,您可以使用锁(或各种其他技术)来同步您的访问。但是,如果您从串行队列中进行所有交互,则不需要锁。
但是您需要小心,因为虽然您使用的是串行队列,但您有可能 运行 在不同线程上运行的完成处理程序。但是,如果您协调这些完成处理程序的交互,以便 none 可以同时 运行(更多信息,请参见下文),那么就不需要锁(或其他同步技术)。
基于您的代码片段(以及我们在其他地方进行的对话)的一系列其他想法:
您仍在使用带有异步方法的 GCD 调度调用。那里存在认知失调。要么
- 完全放弃调度队列并使用完成处理程序模式(恕我直言,这是更好的解决方案,但你说你不能更改你的端点,这显然排除了这种更合乎逻辑的方法);或
- 使
createFile
和 readFile
同步运行(例如,使用信号量),然后您可以利用 GCD 队列行为(我们通常避开锁、信号量或任何可将 "wait",但鉴于您是在后台队列中执行此操作,因此问题较少)。
如果你想让这两个基本异步的任务同步运行,你也可以用锁来实现,但我认为如果你打算使用调度队列,调度信号量更合乎逻辑.
更好的模式是让 createFile
和 readFile
创建自定义的异步 NSOperation
子类。这是比 GCD 中的锁或信号量更强的模式,因为它最大限度地减少了死锁风险。
与此处的问题完全无关,我不建议将 FileObject
设为单例。每个 FileObject
都与一个特定的文件相关联,并且它没有成为单身人士的业务。这一点尤其重要,因为我们在线下发现您正在考虑这是一个有多个请求进入的 XPC 服务,而单例模式与此相反。
另外,顺便说一句,FileObject
不符合使用单例的基本条件。有关单例注意事项的讨论(我们不需要在这里重复,特别是因为它与您的主要问题无关),请参阅 What is so bad about singletons? 单例有其位置,但在这种情况下您可能需要单独的实例,所以单例模式似乎特别不适合。
我的库中公开了 2 个方法调用,如下所示:
-(void) createFile {
dispatch_async(serialQueue, ^{
[fileObj createFile:fileInfo completion:^(void){
//completion block C1
}];
});
}
-(void) readFile:(NSUInteger)timeStamp {
dispatch_async(serialQueue, ^{
[fileObj readFile:timeStamp completion:^(void){
//completion block C2
}];
});
}
现在 createFile:fileInfo:completion
和 readFile:timeStamp:completion
依次是调用进程 P1
的 XPC 调用。它们在进程 P1 中的实现如下所示:
@interface FileObject : NSObject
+ (instancetype) sharedInstance;
@property (nonatomic, strong) NSData *fileContents;
@end
@implementation FileObject
+ (instancetype)sharedInstance
{
static FileObject *sharedObj = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObj = [[self alloc] init];
});
return sharedObj;
}
- (void)createFile:(FileInfo *)fileInfo
completion:(void (^))completion {
FileObject *f = [FileObject sharedInstance];
//lock?
f.fileContents = //call some method;
//unlock
}
- (void)readFile:(NSUInteger)timeStamp
completion:(void (^))completion {
FileObject *f = [FileObject sharedInstance];
//do stuff with f.fileContents
}
@end
需要注意的一点是,对方法createFile
的调用能够对单例obj进行修改,并且readFile:fileInfo
总是在createFile
之后调用(使用串行队列调用时)。
我的问题是我的库中公开的两种方法的串行队列用法,
- 修改
readFileInfo:FileInfo:completion
中的f.fileContents
是否还需要加锁和解锁? - 如果多个不同的进程调用我的库怎么办? XPC
P1
会自动处理还是我必须做些什么?
您问的是:
Do I need to use locks if the methods are already executed in a serial queue?
如果您可以同时从多个线程与非线程安全对象进行交互,是的,您可以使用锁(或各种其他技术)来同步您的访问。但是,如果您从串行队列中进行所有交互,则不需要锁。
但是您需要小心,因为虽然您使用的是串行队列,但您有可能 运行 在不同线程上运行的完成处理程序。但是,如果您协调这些完成处理程序的交互,以便 none 可以同时 运行(更多信息,请参见下文),那么就不需要锁(或其他同步技术)。
基于您的代码片段(以及我们在其他地方进行的对话)的一系列其他想法:
您仍在使用带有异步方法的 GCD 调度调用。那里存在认知失调。要么
- 完全放弃调度队列并使用完成处理程序模式(恕我直言,这是更好的解决方案,但你说你不能更改你的端点,这显然排除了这种更合乎逻辑的方法);或
- 使
createFile
和readFile
同步运行(例如,使用信号量),然后您可以利用 GCD 队列行为(我们通常避开锁、信号量或任何可将 "wait",但鉴于您是在后台队列中执行此操作,因此问题较少)。
如果你想让这两个基本异步的任务同步运行,你也可以用锁来实现,但我认为如果你打算使用调度队列,调度信号量更合乎逻辑.
更好的模式是让
createFile
和readFile
创建自定义的异步NSOperation
子类。这是比 GCD 中的锁或信号量更强的模式,因为它最大限度地减少了死锁风险。与此处的问题完全无关,我不建议将
FileObject
设为单例。每个FileObject
都与一个特定的文件相关联,并且它没有成为单身人士的业务。这一点尤其重要,因为我们在线下发现您正在考虑这是一个有多个请求进入的 XPC 服务,而单例模式与此相反。另外,顺便说一句,
FileObject
不符合使用单例的基本条件。有关单例注意事项的讨论(我们不需要在这里重复,特别是因为它与您的主要问题无关),请参阅 What is so bad about singletons? 单例有其位置,但在这种情况下您可能需要单独的实例,所以单例模式似乎特别不适合。