A mutex blocks only the main thread when it reaches its call with the @synchronized directive
Asked Answered
E

1

0

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.

Encyclopedist answered 4/1, 2012 at 11:28 Comment(7)
Do you have any particular reason for avoiding the baked-in Apple options for threading (i.e. Grand Central Dispatch, NSOperation/NSOperationQueue)?Pang
Actually yes, I need to block the thread that calls any of those methods until it finishes its execution, I'm editing my question to show another solution which I'm trying to avoid.Encyclopedist
Just out of interest, why not static NSString * SubmitChangesLock = @"Submit-Changes-Lock"; instead of playing around with the initialization method?Span
@Encyclopedist - you say you're locking any thread which calls this method - blocking even the main thread? I would seriously look at using NSOperationQueue with maxConcurrency set to 1 and making your app's architecture work asynchronously.Span
I've not tried it, but I guess I've used it that way because this variable should last during the application's life time and I want to avoid to be garbage-collected as it's autoreleased, I'm not sure how the garbage-collector deals with the static variables.Encyclopedist
@Span - I do need to block the thread that calls any of those functions until it's finished, but my problem is that sometimes it keeps blocking the main thread although the execution has finished.Encyclopedist
Well, as there's no garbage collector, you're probably going to be OK :) Strings declared literally like that don't get autoreleased which is probably what you're worried about.Span
P
2

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 and setMaxConcurrentOperationCount: 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.

Pang answered 4/1, 2012 at 12:31 Comment(3)
I know about the NSOperationQueue and I'm using it in my application, but here I have the described problem and I'm aware of the blocking of the main thread but it'll be blocked for approximately no time so I need to keep the thread that call any of the function blocked, I can use performOnMainThread as described after the "edit" in my question. but I'm asking for another solution and more precisely what might be the problem that the mutex doesn't get unlocked.Encyclopedist
@Encyclopedist have you found the root cause of this problem? I mean the reason why mutex not unlockedGordie
@Gordie I am sorry it is quite an old issue and I am 100% sure of what did I end up doing back then, but I think I went with the edit in the question, keeping all the interaction with the database from a single thread (the main one).Encyclopedist

© 2022 - 2024 — McMap. All rights reserved.