ios 锁定一个对象不被多个线程访问 - Objective-C

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10853529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:35:41  来源:igfitidea点击:

Locking an object from being accessed by multiple threads - Objective-C

objective-ciosmultithreading

提问by codeBearer

I have a question regarding thread safety in Objective-C. I've read a couple of other answers, some of the Apple documentation, and still have some doubts regarding this, so thought I'd ask my own question.

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

My question is three fold:

我的问题是三重

Suppose I have an array, NSMutableArray *myAwesomeArray;

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

Fold 1:

折叠1:

Now correct me if I'm mistaken, but from what I understand, using @synchronized(myAwesomeArray){...}will prevent two threads from accessing the same block of code. So, basically, if I have something like:

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

-(void)doSomething {
    @synchronized(myAwesomeArray) {
        //some read/write operation on myAwesomeArray
    }
}

then, if two threads access the samemethod at the sametime, that block of code will be thread safe. I'm guessing I've understood this part properly.

那么,如果两个线程访问相同的方法相同的时间,代码块将是线程安全的。我猜我已经正确理解了这部分。

Fold 2:

折叠2:

What do I do if myAwesomeArrayis being accessed by multiple threads from different methods? If I have something like:

如果myAwesomeArray多个线程从不同的方法访问,我该怎么办?如果我有类似的东西:

- (void)readFromArrayAccessedByThreadOne {
    //thread 1 reads from myAwesomeArray
}

- (void)writeToArrayAccessedByThreadTwo {
    //thread 2 writes to myAwesomeArray
}

Now, both the methods are accessed by two different threads at the same time. How do I ensure that myAwesomeArraywon't have problems? Do I use something like NSLock or NSRecursiveLock?

现在,这两种方法同时被两个不同的线程访问。我如何确保myAwesomeArray不会有问题?我是否使用 NSLock 或 NSRecursiveLock 之类的东西?

Fold 3:

折叠 3:

Now, in the above two cases, myAwesomeArraywas an iVar in memory. What if I have a database file, that I don't always keep in memory. I create a databaseManagerInstancewhenever I want to perform database operations, and release it once I'm done. Thus, basically, different classes can access the database. Each class creates its own instance of DatabaseManger, but basically, they are all using the same, single database file. How do I ensure that data is not corrupted due to race conditions in such a situation?

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

This will help me clear out some of my fundamentals.

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

回答by lawicko

Fold 1Generally your understanding of what @synchronizeddoes is correct. However, technically, it doesn't make any code "thread-safe". It prevents different threads from aquiring the same lock at the same time, however you need to ensure that you always use the same synchronization token when performing critical sections. If you don't do it, you can still find yourself in the situation where two threads perform critical sections at the same time. Check the docs.

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

Fold 2Most people would probably advise you to use NSRecursiveLock. If I were you, I'd use GCD. Here is a great document showing how to migrate from thread programming to GCD programming, I think this approach to the problem is a lot better than the one based on NSLock. In a nutshell, you create a serial queue and dispatch your tasks into that queue. This way you ensure that your critical sections are handled serially, so there is only one critical section performed at any given time.

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

Fold 3This is the same as Fold 2, only more specific. Data base is a resource, by many means it's the same as the array or any other thing. If you want to see the GCD based approach in database programming context, take a look at fmdb implementation. It does exactly what I described in Fold2.

折叠 3这与折叠 2相同,只是更具体。数据库是一种资源,在很多方面它与数组或任何其他东西相同。如果您想在数据库编程上下文中查看基于 GCD 的方法,请查看 fmdb implementation。它完全符合我在Fold2 中的描述。

As a side note to Fold 3, I don't think that instantiating DatabaseManagereach time you want to use the database and then releasing it is the correct approach. I think you should create one single database connection and retain it through your application session. This way it's easier to manage it. Again, fmdb is a great example on how this can be achieved.

作为Fold 3 的旁注,我认为每次要使用数据库然后释放它时都实例化DatabaseManager是正确的方法。我认为您应该创建一个单一的数据库连接并通过您的应用程序会话保留它。这样管理起来就容易多了。同样,fmdb 是一个很好的例子,说明如何实现这一点。

EditIf don't want to use GCD then yes, you will need to use some kind of locking mechanism, and yes, NSRecursiveLockwill prevent deadlocks if you use recursion in your methods, so it's a good choice (it is used by @synchronized). However, there may be one catch. If it's possible that many threads will wait for the same resource and the order in which they get access is relevant, then NSRecursiveLockis not enough. You may still manage this situation with NSCondition, but trust me, you will save a lot of time using GCD in this case. If the order of the threads is not relevant, you are safe with locks.

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

回答by onmyway133

As in Swift 3in WWDC 2016 Session Session 720 Concurrent Programming With GCD in Swift 3, you should use queue

正如斯威夫特3WWDC 2016会话的会话720并发编程随着斯威夫特3 GCD,你应该使用queue

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

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

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

回答by GoZoner

Subclass NSMutableArray to provide locking for the accessor (read and write) methods. Something like:

子类 NSMutableArray 为访问器(读取和写入)方法提供锁定。就像是:

@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end

@implementation MySafeMutableArray

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

// ...
@end

This approach encapsulates the locking as part of the array. Users don't need to change their calls (but may need to be aware that they could block/wait for access if the access is time critical). A significant advantage to this approach is that if you decide that you prefer not to use locks you can re-implement MySafeMutableArray to use dispatch queues - or whatever is best for your specific problem. For example, you could implement addObject as:

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

- (void)addObject:(id)obj {
    dispatch_sync (self.queue, ^{ [super addObject: obj] });
}

Note: if using locks, you'll surely need NSRecursiveLock, not NSLock, because you don't know of the Objective-C implementations of addObject, etc are themselves recursive.

注意:如果使用锁,你肯定需要 NSRecursiveLock,而不是 NSLock,因为你不知道 addObject 的 Objective-C 实现,等等本身是递归的。