I am using background operations heavily and I was just curious if this would ever cause a deadlock. I have a Core Data Managed Object Context set to use a private queue that is referenced (possibly simultaneously) from a few different threads using performblockandwait. Under certain conditions a background task may be kicked off via the completion of another background task.
Something like the following is a possibility since my background tasks are started when certain conditions are met, which could occur from user input on the main thread or a background task.
- (void)task1
{
__weak MyClass *weakself = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0),^{
....
....
[context performBlockAndWait:^{
BOOL condition = [weakself performDbCleanup];
if (condition)
{
[weakself task2];
}
}];
});
}
- (void)task2
{
__weak MyClass *weakself = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0),^{
....
....
[context performBlockAndWait:^{
[weakself performDbCleanup2];
}];
});
}
Its hard to explain exactly why I need to do things this way, but I do need performBlockAndWait in both instances because this is a highly simplified version of what is happening. Long processing happens before and after the performBlockAndWait calls that I don't want blocking access to the DB Context, and also should not be blocking the main thread.
Related
Edit: In fact semaphore works perfectly with background NSOperation, the problem was that sometimes operation becomes cancelled even before it has chance to start, which produced extra call to dispatch_semaphore_signal and allowed passing of two dispatch_semaphore_wait without stopping thread.
My iOS app uses sqlite database as data storage (without CoreData due to historical reasons). sqlite crashes when one database connection is accessed by few threads simultaneously, so to make it possible I use dispatch_semaphore_wait with semaphore activated as dispatch_semaphore_create(1) on database opening.
After I implemented one of database access methods as NSOperation (I need cancellation functional) my app started to crash and I realised that dispatch_semaphore_wait does not pause thread even when database is already busy. This usually happens when I perform quick database call from the main thread which overlaps with bigger call wrapped in to NSOperation.
Here are code which creates NSOperation:
self.loadOperation = [NSBlockOperation blockOperationWithBlock:^{
[database openDatabase];
// read some data
}
self.loadOperation.queuePriority = NSOperationQueuePriorityLow;
self.loadOperation.qualityOfService = NSOperationQualityOfServiceUtility;
self.loadOperation.completionBlock = ^{
[database closeDatabase];
};
NSOperationQueue* backgroundQueue = [[NSOperationQueue alloc] init];
[backgroundQueue addOperation:self.loadOperation];
And here is how I manage access to DB:
- (instancetype)init
{
if ( self = [super init] ) {
databaseOpenSemaphore = dispatch_semaphore_create(1);
}
return self;
}
- (void)openDatabase
{
dispatch_semaphore_wait(databaseOpenSemaphore, DISPATCH_TIME_FOREVER);
// open DB
}
- (void)closeDatabase
{
// close DB
dispatch_semaphore_signal(databaseOpenSemaphore);
}
dispatch_semaphore_wait works with concurrent background NSOperation.
But next time I should consider that background NSOperation with low priority could be cancelled after it's added to a queue but before it has time to start. In such case in my implementation completionBlock for this operation made extra call to dispatch_semaphore_signal which produced "mysterious" fault of dispatch_semaphore_wait
I've got this method
-(void)addObjectToProcess(NSObject*)object;
and i want this method to add the object to process queue which can process up to 4 objects in parallel.
i've created my own dispatch_queue and semhphore
_concurrentQueue = dispatch_queue_create([queue_id UTF8String],DISPATCH_QUEUE_CONCURRENT);
_processSema = dispatch_semaphore_create(4);
and the implementation of the method is:
-(void)addObjectToProcess(NSObject*)object {
dispatch_semaphore_wait(self.processSema, DISPATCH_TIME_FOREVER);
__weak MyViewController* weakSelf = self;
dispatch_async(self.concurrentQueue, ^{
// PROCESS...........
// ..................
dispatch_semaphore_signal(self.processSema);
dispatch_async(dispatch_get_main_queue(), ^{
// call delegate from UI thread
});
});
}
it seems the caller sometimes gets blocked cause of the semaphore barrier.
is there any other/easier option to implement what i'm trying to make here ?
Thanks
The problem is that you're calling dispatch_semaphore_wait on whatever thread you called addObjectToProcess on (presumably the main thread). Thus, if you already have four tasks running, when you schedule this fifth process, it will wait on the main thread.
You don't, though, just want to move the waiting for the semaphore into the block dispatched to self.concurrentQueue, because while that will successfully constrain the "PROCESS" tasks to four at a time, you will consume another worker thread for each one of these backlogged dispatched tasks, and there are a finite number of those worker threads. And when you exhaust those, you could adversely affect other processes.
One way to address this would be to create a serial scheduling queue in addition to your concurrent processing queue, and then dispatch this whole scheduling task asynchronously to this scheduling queue. Thus you enjoy the maximum concurrency on the process queue, while neither blocking the main thread nor using up worker threads for backlogged tasks. For example:
#property (nonatomic, strong) dispatch_queue_t schedulingQueue;
And
self.schedulingQueue = dispatch_queue_create("com.domain.scheduler", 0);
And
- (void)addObjectToProcess(NSObject*)object {
dispatch_async(self.schedulingQueue, ^{
dispatch_semaphore_wait(self.processSema, DISPATCH_TIME_FOREVER);
typeof(self) __weak weakSelf = self;
dispatch_async(self.concurrentQueue, ^{
// PROCESS...........
// ..................
typeof(self) __strong strongSelf = weakSelf;
if (strongSelf) {
dispatch_semaphore_signal(strongSelf.processSema);
dispatch_async(dispatch_get_main_queue(), ^{
// call delegate from UI thread
});
}
});
});
}
Another good approach (especially if the "PROCESS" is synchronous) is to use NSOperationQueue that has a maxConcurrentOperationCount, which controls the degree of concurrency for you. For example:
#property (nonatomic, strong) NSOperationQueue *processQueue;
And initialize it:
self.processQueue = [[NSOperationQueue alloc] init];
self.processQueue.maxConcurrentOperationCount = 4;
And then:
- (void)addObjectToProcess(NSObject*)object {
[self.processQueue addOperationWithBlock:^{
// PROCESS...........
// ..................
dispatch_async(dispatch_get_main_queue(), ^{
// call delegate from UI thread
});
}];
}
The only trick is if the "PROCESS", itself, is asynchronous. If you do that, then you can't just use addOperationWithBlock, but rather have to write your own custom, asynchronous NSOperation subclass, and then use addOperation to the NSOperationQueue. It's not hard to write asynchronous NSOperation subclass, but there are a few little details associated with that. See Configuring Operations for Concurrent Execution in the Concurrency Programming Guide.
What I want to do is create a async core data task on a background thread so as not to chew up the main thread, but I also want to do main thread work once the work is done...
Here's my task
-(void)doTaskwithCompletion:(coreDataCompletion)complete
{
[self.backgroundManagedObjectContext performBlock:^{
// do my BG core data task
[self saveContext:self.backgroundManagedObjectContext];
complete(YES);
}];
}
Here's my block method
[[MYCoreDataManager sharedInstance]doTaskwithCompletion:^(BOOL complete) {
if (complete == YES) {
dispatch_async(dispatch_get_main_queue(), ^{
// back to the main thread
});
}
}];
Something tells me this is wrong... but I can't find another way to put myself back on the main thread once the block has completed... notifications seem way too clunky.
I suppose in a nutshell my question is can I call dispatch_async(dispatch_get_main_queue() inside moc performBlock:^?
Essentially
-(void)doTaskwithCompletion:(coreDataCompletion)complete
{
[self.backgroundManagedObjectContext performBlock:^{
// do my BG core data task
[self saveContext:self.backgroundManagedObjectContext];
dispatch_async(dispatch_get_main_queue(), ^{
// back to the main thread
});
}];
}
I guess you know that it is a very common pattern to call something async and inside go back to mainQueue, i.e. for updating the UI:
dispatch_async(globalQueue, ^{
// do something
dispatch_async(mainQueue, ^{
// update UI
});
});
As you already named your variable self.backgroundManagedObjectContext you have probably heart of Multi-Context CoreData and I understand your concerns. As long as you are not trying to change something with this block for CoreData (on any Context) you are probably fine.
Just make sure that you use the correct initializer for your contexts, i.e. [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
"I suppose in a nutshell my question is can I call dispatch_async(dispatch_get_main_queue() inside moc performBlock:^?"
The answer is yes! In fact, you are doing the REVERSE of that when you call your performBlock from the main thread, right? This is a common feature of iOS. A cleaner way to do it perhaps is passing in a completion, and in that completion calling main...
right now, you have:
dispatch_async(dispatch_get_main_queue(), ^{
// back to the main thread
completion()
});
You could also write:
-(void)doTaskwithCompletion:(coreDataCompletion)complete
{
[self.backgroundManagedObjectContext performBlock:^{
// do my BG core data task
[self saveContext:self.backgroundManagedObjectContext];
complete()
}];
}
where complete has in it a call to the main thread.
I have an IOS project (ARC disabled) which has several view controllers. One particular controller initialises a member object of type MyClass, however when the view controller is dismissed, I'm calling a cleanup method for the object which uses a thread (using dispatch-async) to make some time consuming operations and then when these operations are done im executing a [self release] on the main queue for the object. Is this a good practise, will it cause any errors? Below is a similar example to what im doing:
#implementation ViewController
- (void)viewDidLoad
{
myObj = [[MyClass alloc] init];
}
-(void)viewWillDisappear
{
[myObj cleanup];
}
#end
//myClass
#implementation MyClass
- (void)cleanup()
{
dispatch_queue_t myQueue = dispatch_queue_create ("MyClassDeallocQueue", NULL);
dispatch_async(myQueue, ^{
//time consuming operations
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
}
#end
Is this a good practise, will it cause any errors?
Currently, your code has an unbalanced retain/release. That is definitely an error (over release).
"Is it good practice?" - well, I don't know what you are trying to accomplish. But if your goal is to keep self alive, until after the block is executed, it is already accomplished purely through the fact that self will be captured. So, strictly a release is not needed.
However, if you NOT explicitly release self on the main thread, you introduce a subtle bug: it might happen that the block has the last reference to self, and since it may execute on some arbitrary thread, it will release self on this non-main thread. And this is forbidden: UIKit methods (including dealloc) MUST be called on the main thread!
Thus, it might make sense:
[self retain];
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
or shorter:
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self self];
});
});
Edit:
It's an interesting question, whether the "short" version is actually tread-safe or has a race:
Suppose, self will be released in the block executed on myQueue, as the effect of capturing self before it will be retained in the same bock as an effect of capturing self for the block executed on the main queue. Then, we have an issue. Comments appreciated.
I have a method that builds a package, sends it to a web service, gets a package back, opens it and returns me a nsdictionary. How can I call it on a background queue in order to display a HUD while it requests the data?
You could detach a new thread like following
- (void) fetchData
{
//Show Hud
//Start thread
[NSThread detachNewThreadSelector:#selector(getDataThreaded)
toTarget:self
withObject:nil];
}
- (void) getDataThreaded
{
//Start Fetching data
//Hide hud from main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI if you have to
//Hide Hud
});
}
Grand central dispatch (gcd) provides great support for doing what you ask. Running something in the background using gcd is simple:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0) ^{
NSDictionary* data = [self fetchAndParseData];
dispatch_async(dispatch_get_main_queue(), ^{
[self dataRetrieved:data];
});
});
This call will return immediately (so your UI will continue to be responsive) and dataRetrieved will be called when the data is ready.
Now, depending on how fetchAndParse data works it may need to be more complicated. If you NSURLConnection or something similar, you might need to create an NSRunLoop to process data callbacks on the gcd thread. NSURLConnection for the most part is asynchronous anyway (though callbacks like didReceiveData will be routed through the UI thread) so you can use gcd only to do the parsing of the data when all the data has been retrieved. It depends on how asynchronous you want to be.
In addition to previous replies, why don't you use NSOperation and NSOperationQueue classes? These classes are abstractions under GCD and they are very simple to use.
I like NSOperation class since it allows to modularize code in apps I usually develop.
To set up a NSOperation you could just subclass it like
//.h
#interface MyOperation : NSOperation
#end
//.m
#implementation MyOperation()
// override the main method to perform the operation in a different thread...
- (void)main
{
// long running operation here...
}
Now in the main thread you can provide that operation to a queue like the following:
MyOperation *op = [[MyOperation alloc] initWithDocument:[self document]];
[[self someQueue] addOperation:op];
P.S. You cannot start an async operation in the main method of a NSOperation. When the main finishes, delegates linked with that operations will not be called. To say the the truth you can but this involves to deal with run loop or concurrent behaviour.
Here some links on how to use them.
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
https://developer.apple.com/cocoa/managingconcurrency.html
and obviously the class reference for NSOperation