I'm building a multithreaded application, from which more than one thread can write to an sqlite3 database including the main thread. I declared a static public variable to be used for the mutex:
@implementation Application
#pragma mark -
#pragma mark Static Initializer
static NSString * SubmitChangesLock = nil;
+ (void)initialize {
[super initialize];
SubmitChangesLock = [[NSString alloc] initWithString:@"Submit-Changes-Lock"];
}
+ (NSString *)submitChangesLock {
return SubmitChangesLock;
}
@end
and inside each method that should write to a database I'm using that variable with the @synchronized directive to lock the section that write to the database.
- (void)method1FromClass1 {
@synchronized ([Application submitChangesLock]) {
// write to the database here...
}
}
- (void)method2FromClass2 {
@synchronized ([Application submitChangesLock]) {
// write to the database here...
}
}
and everything worked fine but sometimes when any of those methods gets called from the main thread it freezes waiting for the mutex to be unlocked again but it didn't and the problem is that this only occurs on some calls from the main thread and the code that writes to the database is definitely finite so I could not determine why the main thread keeps waiting for mutex to be unlocked and why it's not getting unlocked on the first place.
Note: none of the other threads got blocked by this mutex, only the main on.
EDIT: I tried to replace the @synchronized directive using the performSelectorOnMainThread:waitUntilDone:
- (void)writeToDatabase {
// write to the database here...
}
- (void)method2FromClass2 {
[self performSelectorOnMainThread:@selector(writeToDatabase) withObject:nil waitUntilDone:YES];
}
and it's working just fine but I'm trying to avoid so much load on the main thread not to block the user interaction.
Any help would be greatly appreciated, and many thanks in advance.
There are features in iOS which exist to help you avoid dealing with threads/locking in simple to moderately complex situations.
If you set up an
NSOperationQueue
andsetMaxConcurrentOperationCount:
to 1, as suggested by deanWombourne, you can offload all the work to a background thread. There's even a handy class (NSInvocationOperation
) to easily reuse your code from existing classes in a queue.If these methods that are running in the background will affect what's appearing in the UI, you can always use
performSelectorOnMainThread:withObject:waitUntilDone:
to update whatever is necessary.If you do it like this, you'll never be blocking your main thread with DB activity. Since blocking the main thread freezes the UI, this is definitely the way to do things.