最近读了Coredata第一三方库Magical Record作者的一篇文章链接描述,对Coredata访问有了新的疑惑,performBlock
API 是如何保证线程安全的?
众所周知 , NSManagedObjectContext(简称moc) 是不允许跨线程访问的 ,用人话说就是 在哪个线程创建的moc ,就在哪个线程使用它.
苹果在iOS5.0之后 推出了一个新的 api 操作moc. 有代码长这样 :
__block BOOL savedOK = NO;
[myMOC performBlock:^{
// Do lots of things with the context.
savedOK = [myMOC save:&error];
}];
即是通过NSPrivateQueueConcurrencyType
类型创建的MOC 系统会为其分配一个私有队列,而不需要我们手动为它创建线程, 从此并发变的更容易 。
但是通过上面那篇文章, Magical Record 作者指出, 苹果的新api 使用了GCD , 而在GCD中的并发我们是通过队列来实现的,即我们只关注和操作队列,而不是直接操作线程。 当你提交一个block代码块到GCD的队列上,队列会自动分配线程去执行这个block。 但是,GCD不能保证这个队列始终在一条线程运行!! 也就是说,当你提交block到队列时,是将任务放到线程A执行,但下一刻,也许任务已经不在线程A而在线程B了!虽然此时依旧在同一个队列!这即是作者弃用contextForCurrentThread
的原因。
那么问题来了,如上文代码中,myMOC是一个NSPrivateQueueConcurrencyType
类型的上下文,创建时系统为其分配了私有队列Q,我们假设myMOC创建发生在线程A,线程A属于队列Q,但由于GCD的特性,某一刻将block任务分配到了属于队列Q的线程B ,也就是说,此时,创建于线程A的myMOC 可能会在线程B被访问 , 这就发生了跨线程访问!
这又如何解释呢? 希望对这部分了解的大虾给予解答,如果我解决了这个问题,也会在楼下贴出 。
PHP中文网2017-04-17 18:00:30
답변해 주셔서 감사합니다. 그런데 두 가지 질문이 더 있는 것 같습니다.
1. 작성자의 글에는 "Apple의 새로운 API는 GCD를 사용한다"라고 직접적으로 언급하지 않았습니다. 원문에 However, now, as more of you (and the heart of MagicalRecord) is using GCD
라는 문장이 있는데, 이는 MagicalRecord의 핵심이 GCD를 사용한다는 의미입니다. MagicalRecord에서는 비동기 작업을 구현하기 위해 GCD 스케줄링 큐를 사용하는 방법이 발견되지 않은 것으로 나타났습니다. 코어는 MOC에서 비동기 작업을 구현하기 위해 performBlock
및 performBlockAndWait
을 사용합니다. GCD와 관련이 있는데 왜 당연히 GCD를 사용하지 않는 걸까요? 그런데 GCD의 특성상 이전 인터페이스 performBlock
를 포기한 걸까요?
contextForCurrentThread
2. 질문 1을 제쳐두고 Apple 문서의 일부에 다음과 같이 나와 있습니다.
NSPrivateQueueConcurrencyType 유형의 MOC는 초기화 중에 자체 대기열을 생성합니다. 이 대기열은 이 MOC 내에서 비공개입니다. 따라서 다음과 같이 이해될 수 있습니다.
스레드 a에서의 MOC를 생성했는데, 이는 개인 큐를 생성하고 스레드 b의
아래에 있는 콘텐츠를 관리합니다.NSPrivateQueueConcurrencyType
으아아아
performBlock
스레드 a에서 컨텍스트가 생성되면 스레드 a에 속해야 하지만 대기열의 스레드 b에서 액세스됩니다. 크로스 스레드 액세스가 아닌가요?
은 현재 컨텍스트가 속한 스레드에서의 실행을 보장하는
과 충돌합니까? 개인 큐를 생성할 때 b와 a가 동일한 스레드인지 어떻게 보장됩니까? 어떻게 하나요?PHPz2017-04-17 18:00:30
링크해주신 기사를 봤는데, "그런데 위 기사를 통해 Magical Record 작성자가 Apple의 새로운 API가 GCD를 사용한다는 점을 지적했는데, 우리는 큐를 통해 GCD에서 동시성을 구현하고 있습니다."라는 설명은 보지 못했습니다. "Apple의 새로운 API는 GCD를 사용합니다"라는 위의 문장.
다음은 참조된 Apple 문서입니다. PerformBlock 메서드는 현재 컨텍스트가 속한 스레드에서 실행되도록 합니다.
으아아아