views:

41

answers:

2

What is the best way to synchronize a data source which is updated frequently from a background thread with the GUI main thread?

Should i put a pthread mutex around each method call? This seems to be pretty heavy too me.

EDIT: I'm looking for a 10.5 solution

+1  A: 

You could always update the model and table view on the main thread. There are functions in NSObject like performSelectorOnMainThread:withObject:waitUntilDone: that allows you to easily perform a function call on the main thread. So you could update your model with:

[model performSelectorOnMainThread:@selector(addObject:) withObject:newObject waitUntilDone:YES];

and then, update your tableview with this:

[tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];

Things get more complicated if you need to pass more than one object, because then you need to use invocations, but I've found that these functions can be used most of the time.

Joe Ibanez
Well but my main idea was to take work load from the main thread. The model can have a few ten thousand of list items. And you can feel it when the main thread blocks even for 10ms.
Lothar
Presumably, creating the objects to go in the array would take the most time, not inserting them into the array. If you're concerned about the load on the main thread adding objects individually, you could batch add several at a time with addObjectsFromArray:. Otherwise, without using GCD (which you can't if you're making a 10.5 application), you'd have to use an NSLock.
Joe Ibanez
A: 

Is this on Snow Leopard or do you wish to retain compatibility with 10.2+? If you're dead-set on keeping backwards compatibility, you can factor out the code that applies the updates to another method and call it using performSelectorOnMainThread:withObject:waitUntilDone:

Alternatively, if you'd prefer to play with new toys and keep your code more readable (i.e. keep the number of methods to a minimum), you could do it in-line courtesy of blocks and Grand Central Dispatch. Something like the following might suffice:

// The following line is (presumably) executed in your background thread
NSMutableArray *newEntries = [self doSomeHeavyLiftingInBackground];
dispatch_async(dispatch_get_main_queue(), ^{
    /* The following lines are executed in the context of your main 
    thread's run loop. You don't need to mess about with locks as it
    will not be executed concurrently with UI updates. */
    id<NSTableViewDataSource> dataSource = [self getDataSource];
    NSMutableArray *dataSourceInnards = [dataSource whyIsThisMethodHere];
    [dataSourceInnards addObjectsFromArray:newEntries];
    [dataSource tellTableViewToReload];
});

This has the advantage of not having to contort your code to the pass-a-single-object-to-a-separate-method pattern.

Sedate Alien
Beaten to the punch by a few seconds, but hopefully this highlights a few alternatives. :)
Sedate Alien