2014-12-20 5 views
0

업데이트 나는 objectWithID 잠재적 부모 (조부모 등) 그래서 waitUntilAllOperationsAreFinished 같은 것을 사용하여 부모 스레드를 차단하지 않도록 가져 오는 몇 가지 작업을 수행하는 문맥의 스레드를 필요로 할 수 있음을 확인할 수 있습니다.waitUntilAllOperationsAreFinished 및 objectWithID

빠른 테스트에서 나는 아이들의 moc의 부모를 그들의 조부모에게 대신 지적했고, 원래의 부모를 막는 아이들 스레드를 남겼다. 이 설정에서는 교착 상태가 발생하지 않았습니다. 이것은 아키텍처가 좋지 않기 때문에 재구성 될 것입니다.

원래 질문

나는 NSOperationQueue의 두 개의 층이있다. 첫 번째는 NSOperation 그래프이며 그 사이에는 일련의 종속성이있는 작업이 있습니다. 그들은 모두 서로 교착 상태없이 잘 돌아갑니다. 이러한 작업 중 하나 (사람들 그룹에 대한 스케줄러) 내에서 다른 NSOperationQueue에서 실행될 수있는보다 개별적인 청크로 작업을 분리했습니다. 그러나 더 큰 작업이 완료된 것으로 간주되기 전에 스케줄러가 스케줄을 모두 작성하기를 원할 것입니다. 이를 위해 일단 모든 스케줄 작업을 만들고 스케줄러 작업 큐에 추가하면 작업 대기열에서 waitUntilAllOperationsAreFinished를 호출합니다. 이것은 내가 교착 상태에있는 곳입니다.

나는 변경 내용을 병합하는 부모 컨텍스트를 기다리고 마침내 performBlockAndWait를 사용하여 제공된 블록을 호출하고하는 PrivateQueueConcurrencyType 아이 컨텍스트를 생성, 코어 데이터를 사용하여 부모 관리 개체 컨텍스트를 복용의 루틴을 처리 BlockOperation를라는 NSBlockOperation 서브 클래스가 있어요 . 여기에 몇 가지 코드가 있습니다.

init(block: (NSManagedObjectContext?) -> Void, withDependencies dependencies: Array<NSOperation>, andParentManagedObjectContext parentManagedObjectContext: NSManagedObjectContext?) { 
    self.privateContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) 

    super.init() 

    self.queuePriority = NSOperationQueuePriority.Normal 
    addExecutionBlock({ 
     if (parentManagedObjectContext != nil) { 
      self.parentContext = parentManagedObjectContext! 

      self.privateContext.parentContext = parentManagedObjectContext! 

      self.privateContext.performBlockAndWait({() -> Void in 
       block(self.privateContext) 
      }) 

      self.parentContext!.performBlockAndWait({() -> Void in 
       var error: NSError? 
       self.parentContext!.save(&error) 
      }) 
     } 
    }) 

    for operation in dependencies { 
     addDependency(operation) 
    } 
} 

이것은 이미 저에게 잘 돌아가고 있습니다. 하지만 이제는 작업 대기열에서 모든 작업이 완료 될 때까지 호출 스레드를 차단하려고합니다. 이처럼 ...

for group in groups { 
    let groupId = group.objectID 
    let scheduleOperation = BlockOperation(
     block: { (managedObjectContext: NSManagedObjectContext?) -> Void in 
      ScheduleOperation.scheduleGroupId(groupId, inManagedObjectContext: managedObjectContext!) 
     }, 
     withDependencies: [], 
     andParentManagedObjectContext: managedObjectContext) 
    scheduleOperationQueue.addOperation(scheduleOperation) 
} 

scheduleOperationQueue.waitUntilAllOperationsAreFinished() 

...이 스레드는 마지막 줄에 붙어 있습니다 (분명히). 그러나 다른 스레드가 특정 지점을 지나서 어떤 진전을 이루는 것을 결코 보지 못합니다. 디버거 일시 중지 대기중인 작업이 멈추는 위치를 확인합니다. 그것은 주어진 id를 사용하여 그룹을 가져 오는 ScheduleOperation의 init 메소드에 있습니다.

convenience init(groupId: NSManagedObjectID, inManagedObjectContext managedObjectContext: NSManagedObjectContext) { 

    let group = managedObjectContext.objectWithID(groupId) as Group 
    ... 

합니까 objectWithID 부모의 MOC는과 관련된 따라서 교착 상태를 만드는 것을 "부모"스레드에서 코드를 실행해야합니다 (ScheduleOperation.scheduleGroupId이 초기화 호출)? 이 문제의 원인이 될 수있는 접근 방법이 있습니까?

참고 :이 글은 Swift라고 쓰여 있지만 Objective-C를 태그로 추가했습니다. 이는 언어 관련 문제는 아니지만 프레임 워크 고유의 것으로 느껴지기 때문입니다.

답변

1

일반적으로 스레드 objectWithID이 호출 될 지정되지 않았습니다. 구현 세부 사항입니다. 나는 (비록 상황이 다르더라도) 과거에 핵심 데이터 교착 상태에 몇 가지 문제가 있었고 NSManagedObjectContext에 메서드를 호출 할 때 프레임 워크가 내부적으로 일부 잠금을 수행한다는 것을 알았습니다. 그렇습니다. 교착 상태가 될 수도 있습니다.

아키텍처를 다시 설계하는 것 외에 다른 조언은 없지만 약간 단순화 할 수 있습니다. 컨텍스트와 연결된 비공개 직렬 대기열을 이미 갖고 있으므로 작업이 지정된 순서로 호출된다는 것을 명심하십시오. 따라서 모든 ScheduleOperation 인스턴스간에 동일한 컨텍스트를 공유 할 수 있습니다.scheduleOperationQueue.maxConcurrentOperationsCount을 1로 설정하면 작업이 차례로 실행됩니다. 그리고 호출 스레드를 차단하는 대신 마지막 작업이 완료되면 완료 핸들러를 호출하십시오 (oepration의 completionBlock을 사용할 수 있음).

+0

안녕하세요. Michael, 저는 약간의 변경이 필요한 아키텍처에 동의합니다. 나는 그것을보기 시작할 것입니다. 나는 이러한 작업을 병렬로 실행할 수있는 것을 좋아하므로 변경할 수는 없지만 차질 지점 구현을위한 경쟁 처리기를 추가 할 것입니다. – rob5408