锁定多线程访问的对象-Objective-C

锁定多线程访问的对象-Objective-C,objective-c,ios,multithreading,Objective C,Ios,Multithreading,我对Objective-C中的线程安全性有一个问题。我已经阅读了一些其他答案,一些苹果文档,对此仍有一些疑问,所以我想问我自己的问题 我的问题有三个方面: 假设我有一个数组,NSMutableArray*myAwesomeArray 折叠1: 如果我弄错了,请纠正我,但据我所知,使用@synchronized(myAwesomeArray){…}将阻止两个线程访问同一代码块。所以,基本上,如果我有这样的东西: -(void)doSomething { @synchronized(myAw

我对Objective-C中的线程安全性有一个问题。我已经阅读了一些其他答案,一些苹果文档,对此仍有一些疑问,所以我想问我自己的问题

我的问题有三个方面:

假设我有一个数组,
NSMutableArray*myAwesomeArray

折叠1:

如果我弄错了,请纠正我,但据我所知,使用
@synchronized(myAwesomeArray){…}
将阻止两个线程访问同一代码块。所以,基本上,如果我有这样的东西:

-(void)doSomething {
    @synchronized(myAwesomeArray) {
        //some read/write operation on myAwesomeArray
    }
}
- (void)readFromArrayAccessedByThreadOne {
    //thread 1 reads from myAwesomeArray
}

- (void)writeToArrayAccessedByThreadTwo {
    //thread 2 writes to myAwesomeArray
}
@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end

@implementation MySafeMutableArray

- (void)addObject:(id)obj {
  [self.lock lock];
  [super addObject: obj];
  [self.lock unlock];
}

// ...
@end
然后,如果两个线程在相同的时间访问相同的方法,则该代码块将是线程安全的。我想我已经完全理解了这一部分

折叠2:

如果
myAwesomeArray
被来自不同方法的多个线程访问,我该怎么办? 如果我有类似于:

-(void)doSomething {
    @synchronized(myAwesomeArray) {
        //some read/write operation on myAwesomeArray
    }
}
- (void)readFromArrayAccessedByThreadOne {
    //thread 1 reads from myAwesomeArray
}

- (void)writeToArrayAccessedByThreadTwo {
    //thread 2 writes to myAwesomeArray
}
@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end

@implementation MySafeMutableArray

- (void)addObject:(id)obj {
  [self.lock lock];
  [super addObject: obj];
  [self.lock unlock];
}

// ...
@end
现在,两个不同的线程可以同时访问这两个方法。如何确保
myAwesomeArray
不会出现问题?我是否使用NSLock或NSRecursiveLock之类的工具

折叠3:

现在,在上述两种情况下,
myAwesomeArray
是内存中的iVar。如果我有一个数据库文件,我并不总是把它保存在内存中。每当我想执行数据库操作时,我都会创建一个
databaseManagerInstance
,并在完成后释放它。因此,基本上,不同的类可以访问数据库。每个类都创建自己的
DatabaseManger
实例,但基本上,它们都使用相同的单个数据库文件。在这种情况下,如何确保数据不会因竞争条件而损坏


这将帮助我理清一些基础知识。

子类NSMutableArray为访问器(读写)方法提供锁定。比如:

-(void)doSomething {
    @synchronized(myAwesomeArray) {
        //some read/write operation on myAwesomeArray
    }
}
- (void)readFromArrayAccessedByThreadOne {
    //thread 1 reads from myAwesomeArray
}

- (void)writeToArrayAccessedByThreadTwo {
    //thread 2 writes to myAwesomeArray
}
@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end

@implementation MySafeMutableArray

- (void)addObject:(id)obj {
  [self.lock lock];
  [super addObject: obj];
  [self.lock unlock];
}

// ...
@end
这种方法将锁定封装为数组的一部分。用户不需要更改呼叫(但可能需要注意,如果访问时间紧迫,他们可能会阻止/等待访问)。这种方法的一个显著优点是,如果您决定不使用锁,您可以重新实现MySafeMutableArray以使用调度队列,或者任何最适合您的特定问题的方法。例如,您可以将addObject实现为:

- (void)addObject:(id)obj {
    dispatch_sync (self.queue, ^{ [super addObject: obj] });
}
注意:如果使用锁,您肯定需要NSRecursiveLock,而不是NSLock,因为您不知道addObject等的Objective-C实现本身是递归的

折叠1 一般来说,您对
@synchronized
的理解是正确的。然而,从技术上讲,它并不能使任何代码“线程安全”。它防止不同线程同时获得相同的锁,但是您需要确保在执行关键部分时始终使用相同的同步令牌。如果不这样做,您仍然会发现自己处于两个线程同时执行关键部分的情况

折叠2 大多数人可能会建议您使用NSRecursiveLock。如果我是你,我会用GCD,我认为这种解决问题的方法比基于NSLock的方法要好得多。简而言之,创建一个串行队列并将任务分派到该队列中。通过这种方式,您可以确保您的关键部分是按顺序处理的,因此在任何给定的时间只有一个关键部分被执行

折叠3 这与折叠2相同,只是更具体。数据库是一种资源,从许多方面来说,它与数组或任何其他东西是相同的。它完全符合我在Fold2中描述的

作为Fold 3的补充说明,我不认为在每次需要使用数据库时实例化DatabaseManager然后发布它是正确的方法。我认为您应该创建一个数据库连接,并在应用程序会话中保留它。这样更容易管理它。同样,fmdb是如何实现这一点的一个很好的例子

编辑
如果不想使用GCD,那么是的,您将需要使用某种锁定机制,是的,
NSRecursiveLock
将防止死锁,如果您在方法中使用递归,那么这是一个不错的选择(它由
@synchronized
使用)。然而,可能有一个陷阱。如果许多线程可能会等待相同的资源,并且访问顺序是相关的,那么
NSRecursiveLock
是不够的。您仍然可以使用
NSCondition
来管理这种情况,但是相信我,在这种情况下使用GCD可以节省大量时间。如果线程的顺序不相关,则使用锁是安全的。

与中的Swift 3一样,您应该使用
队列

class MyObject {
  private let internalState: Int
  private let internalQueue: DispatchQueue

  var state: Int {
    get {
      return internalQueue.sync { internalState }
    }

    set (newValue) {
      internalQueue.sync { internalState = newValue }
    }
  }
}

@synchronize
阻止其他线程访问您锁定的同一变量,而不是特定的代码块。啊。我懂了。嗯,我想我对@synchronize指令的理解还有一点。谢谢D@codeBearer我很快就回答了你的问题。谢谢你的回答。如果要创建对象的线程安全子类,这是一个好主意。:)如果您将线程安全留给调用方(也就是说,您没有创建线程安全的子类,不管它是如何实现的),那么您将,重复,为自己设置问题。你可以学习一些旋转技术。祝你好运,太棒了。谢谢你的提示。我会调查的
NSArray
的DAny子类也需要实现其他几种方法;这不是一个好例子。@ilyan。如果你没有提供任何细节来证实你的观点,你的评论也不是一个好的评论。你能说得更具体一点吗?非常感谢你的回复!我想这解决了我大部分的疑问。作为确认:我猜我会使用