锁定对象不被多个线程访问 - Objective-C [英] Locking an object from being accessed by multiple threads - Objective-C

查看:129
本文介绍了锁定对象不被多个线程访问 - Objective-C的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

My问题是三折

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

折叠1:

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

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:

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

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

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

折叠2:

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

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

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

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

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

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

折叠3:

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

Now, in the above two cases, myAwesomeArray was an iVar in memory. What if I have a database file, that I don't always keep in memory. I create a databaseManagerInstance whenever 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?

这将帮助我清除一些基本原则。

This will help me clear out some of my fundamentals.

推荐答案

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

Fold 1 Generally your understanding of what @synchronized does 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.

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

Fold 2 Most 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.

折叠3
这与 Fold 2 相同,只是更具体。数据库是一种资源,通过许多方式它与数组或任何其他东西相同。 如果您想在数据库编程环境中查看基于GCD的方法,请查看fmdb实现。它完全按照我在 Fold2 中描述的那样。

Fold 3 This 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.

作为 Fold 3 的旁注,我不是认为每次要使用数据库然后释放它时实例化 DatabaseManager 是正确的方法。我认为你应该创建一个单一的数据库连接并通过你的应用程序会话保留它。这样就可以更轻松地进行管理。再一次,fmdb就是如何实现这一点的一个很好的例子。

As a side note to Fold 3, I don't think that instantiating DatabaseManager each 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.

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

Edit If don't want to use GCD then yes, you will need to use some kind of locking mechanism, and yes, NSRecursiveLock will 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 NSRecursiveLock is 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.

这篇关于锁定对象不被多个线程访问 - Objective-C的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆