How does this cause deadlock (once in a while) and

2020-04-30 16:10发布

问题:

@synchronized (self.class)     {
    fetchedObjects = [moc executeFetchRequest:request error:&error];
}

moc is a managedObjectContext

It can be the moc that correspond to the main thread. That moc is the parent of all MOC.

The other moc are just children. Sometimes when the child do executeFetchRequest it will also make the parent moc do the same executeFetchRequest.

What I often see is

outside main thread:

@synchronized (self.class)     {
    fetchedObjects = [moc executeFetchRequest:request error:&error]; //semaphore_wait_trap here
}

On main thread

@synchronized (self.class)     {//_psynch_mutexwait
    fetchedObjects = [moc executeFetchRequest:request error:&error];
}

Okay... Why the deadlock? Even though the moc on main thread correspond to the main thread it's not being accessed at all. It wait at @synchronized. So why fetchedObjects = [moc executeFetchRequest:request error:&error]; wait?

回答1:

You shouldn't be @synchronize-ing calls to executeFetchRequest:

The executeFetchRequest:error: method intrinsically scales its behavior appropriately for the hardware and work load. If necessary, the Core Data will create additional private threads to optimize fetching performance. You will not improve absolute fetching speed by creating background threads for the purpose. It may still be appropriate, however, to fetch in a background thread or queue to prevent your application’s user interface from blocking. This means that if a fetch is complicated or returns a large amount of data, you can return control to the user and display results as they arrive.

Core Data Programming Guide: Concurrency

Basically if you're returning a lot of objects and processing them, it's best to do that from a private queue context (since you can then use the returned objects on that private queue and process them off the main queue).

If you have a main queue context, only use it from the main queue.

Also, child contexts execute fetch requests by passing them to their parents. What happens (from what I can tell) is that the predicate gets evaluated on the persistent store (SQL), and on the unsaved objects in each MOC in the chain. This means that if you, say, override a getter that is used in a predicate, it will be called on those unsaved objects in memory (whereas the SQL predicate uses the raw DB values to compare against). You can deadlock a parent context by blocking its queue from a child context.



回答2:

The moc is holding its own lock so you have a lock in a lock -- now when t1 comes, he gets lock 1 but maybe not lock because somebody is fetching already and has lock2 but that one is blocked by t1 (which is waiting)