Multi-threaded core data


#1

There is a topic that the book does not cover (Grand Central Dispatch and threading), but I needed to perform imports with large data sets. I was able to do this efficiently using dispatch and the main and global queue features of GCD. However, I was having many issues with the NSUndoManager due to the fact that controls were bound to a managed object context that was rapidly changing. I ended up creating a separate MOC for my worker thread per the following URL:

duckrowing.com/2010/03/11/us … e-threads/

I think that the above link should be required reading for anyone attempting to work with Core Data and multiple threads. FWIW, I ended up writing a utility class for working with GCD. I’ve included the header and the impl inline:

Dispatch.h

//
//  Dispatch.h
//  Longbox
//
//  Created by Schley Andrew Kutz on 8/29/10.
//  Copyright 2010 lostcreations. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <dispatch/queue.h>

@interface Dispatch : NSObject 
{
}

/**
 * Dispatches the block on a given queue.
 *
 * @param block The block to dispatch.
 * @param queue The queue to use.
 * @param wait A flag indicating whether to use dispatch_sync or dispatch_async.
 *
 */
+(void)dispatch:(dispatch_block_t)block 
      withQueue:(dispatch_queue_t)queue 
        andWait:(BOOL)wait;

/**
 * Dispatches the block on the main queue.
 *
 * @param andWait A flag indicating whether to use dispatch_sync or dispatch_async.
 */
+(void)dispatchOnMainQueue:(dispatch_block_t)block 
                   andWait:(BOOL)wait;

/**
 * Dispatches the block on a global queue.
 *
 * @param priority The priority of the global queue to get. Valid values are:
 *
 *                 DISPATCH_QUEUE_PRIORITY_HIGH    = -2
 *                 DISPATCH_QUEUE_PRIORITY_DEFAULT =  0
 *                 DISPATCH_QUEUE_PRIORITY_LOW     =  2
 *
 * @param andWait A flag indicating whether to use dispatch_sync or dispatch_async.
 */
+(void)dispatchOnGlobalQueue:(dispatch_block_t)block 
                withPriority:(int)priority 
                     andWait:(BOOL)wait;

/**
 * Posts a notification using the default NSNotificationCenter on the main 
 * dispatch queue.
 * 
 * @param notification The notification to post.
 * @param andWait A flag indicating whether to use dispatch_sync or dispatch_async.
 */
+(void)postOnMainQueue:(NSNotification*)notification 
               andWait:(BOOL)wait;

/**
 * Posts a notification using the default NSNotificationCenter on a 
 * global dispatch queue.
 * 
 * @param notification The notification to post.
 * @param andWait A flag indicating whether to use dispatch_sync or dispatch_async.
 */
+(void)postOnGlobalQueue:(NSNotification*)notification
                 andWait:(BOOL)wait;

/**
 * Enqueues a notification using the default NSNotificationQueue on the 
 * main dispatch queue.
 *
 * @param notification The notification to enqueue.
 * @param withPostingStyle The posting style to use. Specify 'nil' to use NSPostASAP.
 * @param mask The coalesce mask to use. Specify 'nil' to use NSNotificationNoCoalescing.
 * @param andWait A flag indicating whether to use dispatch_sync or dispatch_async.
 */
+(void)enqueueOnMainQueue:(NSNotification*)notification 
         withPostingStyle:(NSPostingStyle)style 
      andWithCoalesceMask:(NSNotificationCoalescing)mask 
                  andWait:(BOOL)wait;

/**
 * Enqueues a notification using the default NSNotificationQueue on a
 * global dispatch queue.
 *
 * @param notification The notification to enqueue.
 * @param withPostingStyle The posting style to use. Specify 'nil' to use NSPostASAP.
 * @param mask The coalesce mask to use. Specify 'nil' to use NSNotificationNoCoalescing.
 * @param andWait A flag indicating whether to use dispatch_sync or dispatch_async.
 */
+(void)enqueueOnGlobalQueue:(NSNotification*)notification 
           withPostingStyle:(NSPostingStyle)style 
        andWithCoalesceMask:(NSNotificationCoalescing)mask 
                    andWait:(BOOL)wait;

/**
 * Gets the main dispatch queue.
 *
 * @return The main dispatch queue.
 */
+(dispatch_queue_t)mainQueue;

/**
 * Gets a global dispatch queue with a priority of
 * DISPATCH_QUEUE_PRIORITY_DEFAULT.
 *
 * @return A global dispatch queue.
 */
+(dispatch_queue_t)globalQueue;

/**
 * Gets a global dispatch queue.
 *
 * @param priority The priority of the queue to get. Valid values are:
 *
 *                 DISPATCH_QUEUE_PRIORITY_HIGH    = -2
 *                 DISPATCH_QUEUE_PRIORITY_DEFAULT =  0
 *                 DISPATCH_QUEUE_PRIORITY_LOW     =  2
 *
 * @return A global dispatch queue.
 */
+(dispatch_queue_t)globalQueueWithPriority:(int)priority;

@end

Dispatch.m

//
//  Dispatch.m
//  Longbox
//
//  Created by Schley Andrew Kutz on 8/29/10.
//  Copyright 2010 lostcreations. All rights reserved.
//

#import "Dispatch.h"
#import "NSObject.h"

@implementation Dispatch

+(void)dispatch:(dispatch_block_t)block 
      withQueue:(dispatch_queue_t)queue 
        andWait:(BOOL)wait
{
    if (wait)
    {
        dispatch_sync(queue, block);
    }
    else 
    {
        dispatch_async(queue, block);
    }
}

+(void)dispatchOnMainQueue:(dispatch_block_t)block 
                   andWait:(BOOL)wait
{
    [Dispatch dispatch:block 
             withQueue:[Dispatch mainQueue]
               andWait:wait];
}

+(void)dispatchOnGlobalQueue:(dispatch_block_t)block 
                withPriority:(int)priority 
                     andWait:(BOOL)wait
{
    [Dispatch dispatch:block 
             withQueue:[Dispatch globalQueueWithPriority]
               andWait:wait];
}

+(void)postOnMainQueue:(NSNotification*)notification
               andWait:(BOOL)wait
{
    [Dispatch dispatchOnMainQueue:^(void)
     {
         [[self nc] postNotification:notification];
         //log4Debug(@"Posted %@", notification);
     }
                          andWait:wait];
}

+(void)postOnGlobalQueue:(NSNotification*)notification 
                 andWait:(BOOL)wait
{
    [Dispatch dispatchOnGlobalQueue:^(void)
     {
         [[self nc] postNotification:notification];
         //log4Debug(@"Posted %@", notification);
     }
                       withPriority:DISPATCH_QUEUE_PRIORITY_DEFAULT
                            andWait:wait];
}

+(void)enqueueOnMainQueue:(NSNotification*)notification 
         withPostingStyle:(NSPostingStyle)style 
      andWithCoalesceMask:(NSNotificationCoalescing)mask 
                  andWait:(BOOL)wait
{
    [Dispatch dispatchOnMainQueue:^(void)
     {
         [[self nq] enqueueNotification:notification 
                           postingStyle:style ? style : NSPostASAP 
                           coalesceMask:mask ? mask : NSNotificationNoCoalescing
                               forModes:nil];
         //log4Debug(@"Enqueued %@", notification);
     }
                            andWait:wait];
}

+(void)enqueueOnGlobalQueue:(NSNotification*)notification 
           withPostingStyle:(NSPostingStyle)style 
        andWithCoalesceMask:(NSNotificationCoalescing)mask 
                    andWait:(BOOL)wait
{
    [Dispatch dispatchOnGlobalQueue:^(void)
    {
        [[self nq] enqueueNotification:notification 
                          postingStyle:style ? style : NSPostASAP 
                          coalesceMask:mask ? mask : NSNotificationNoCoalescing
                               forModes:nil];
        //log4Debug(@"Enqueued %@", notification);
    }
                       withPriority:DISPATCH_QUEUE_PRIORITY_DEFAULT
                            andWait:wait];
}

+(dispatch_queue_t)mainQueue
{
    return dispatch_get_main_queue();
}

+(dispatch_queue_t)globalQueue
{
    return [Dispatch globalQueueWithPriority:DISPATCH_QUEUE_PRIORITY_DEFAULT];
}

+(dispatch_queue_t)globalQueueWithPriority:(int)priority
{
    return dispatch_get_global_queue(priority, 0);
}

@end

Hope this helps!


-a

(FWIW, you can see what I’m talking about in my class implementation at http://longboxapp.svn.sourceforge.net/viewvc/longboxapp/longboxapp/trunk/Classes/Library.m?revision=13&view=markup)