如果方法已经在串行队列中执行,我是否需要使用锁?

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:completionreadFile: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之后调用(使用串行队列调用时)。

我的问题是我的库中公开的两种方法的串行队列用法,

  1. 修改readFileInfo:FileInfo:completion中的f.fileContents是否还需要加锁和解锁?
  2. 如果多个不同的进程调用我的库怎么办? XPC P1 会自动处理还是我必须做些什么?

您问的是:

Do I need to use locks if the methods are already executed in a serial queue?

如果您可以同时从多个线程与非线程安全对象进行交互,是的,您可以使用锁(或各种其他技术)来同步您的访问。但是,如果您从串行队列中进行所有交互,则不需要锁。

但是您需要小心,因为虽然您使用的是串行队列,但您有可能 运行 在不同线程上运行的完成处理程序。但是,如果您协调这些完成处理程序的交互,以便 none 可以同时 运行(更多信息,请参见下文),那么就不需要锁(或其他同步技术)。

基于您的代码片段(以及我们在其他地方进行的对话)的一系列其他想法:

  1. 您仍在使用带有异步方法的 GCD 调度调用。那里存在认知失调。要么

    • 完全放弃调度队列并使用完成处理程序模式(恕我直言,这是更好的解决方案,但你说你不能更改你的端点,这显然排除了这种更合乎逻辑的方法);或
    • 使 createFilereadFile 同步运行(例如,使用信号量),然后您可以利用 GCD 队列行为(我们通常避开锁、信号量或任何可将 "wait",但鉴于您是在后台队列中执行此操作,因此问题较少)。
  2. 如果你想让这两个基本异步的任务同步运行,你也可以用锁来实现,但我认为如果你打算使用调度队列,调度信号量更合乎逻辑.

  3. 更好的模式是让 createFilereadFile 创建自定义的异步 NSOperation 子类。这是比 GCD 中的锁或信号量更强的模式,因为它最大限度地减少了死锁风险。

  4. 与此处的问题完全无关,我不建议将 FileObject 设为单例。每个 FileObject 都与一个特定的文件相关联,并且它没有成为单身人士的业务。这一点尤其重要,因为我们在线下发现您正在考虑这是一个有多个请求进入的 XPC 服务,而单例模式与此相反。

    另外,顺便说一句,FileObject 不符合使用单例的基本条件。有关单例注意事项的讨论(我们不需要在这里重复,特别是因为它与您的主要问题无关),请参阅 What is so bad about singletons? 单例有其位置,但在这种情况下您可能需要单独的实例,所以单例模式似乎特别不适合。