If libsqlite is not thread safe a code like that
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__block NSArray *__albumsCollection = albumCollections;
dispatch_apply(count, queue, ^(size_t i)
{
MPMediaItem *albumObj = [[__albumsCollection objectAtIndex:i] representativeItem];
///// making some sqlite queries
});
would raise a BAD_EXEC.
So how to make this code thread safe?
My solution was using the main queue
dispatch_apply(count, dispatch_get_main_queue(), ^(size_t i)
{
/// my sqllite queries
});
but I'm not satisfied with that. How to make it better?
Instead of using dispatch_get_main_queue() to get the main queue you might want to create a separate private dispatch queue on a non-main thread like so:
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_SERIAL); // or NULL as last parameter if prior to OS X 10.7/iOS 5.0
dispatch_apply(count, queue, ^(size_t i) {
/// your SQLite queries
});
Alternatively you could use a FMDatabaseQueue from Gus Mueller's (#ccgus) brilliant FMDB SQLite wrapper framework (which is what I'd do):
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
// Your SQLite queries:
[db executeQuery:#"...", ...];
...
}];
…which will send your query block to a serial dispatch queue, wrapping its execution synchronously.
Not convinced yet?
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Your SQLite queries:
[db executeQuery:#"...", ...];
...
}];
How about now?
Also, custom-defined block-based SQLite functions.
dispatch_queue_t q=dispatch_queue_create("name", NULL);
dispatch_sync(q, ^{
//your code is here
});
dispatch_async(dispatch_get_main_queue(), ^(void) {
//as soon as above block is completed, this block executed and you will be notified that
//work is completed
});
Related
Can anybody see a reason why this code would work fine to update UI:
__block NSDictionary *result = nil;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[SomeService sharedInstance] doSomethingGreatWithReplyBlock:^(NSDictionary * response) {
result = response;
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
[self updateDisplay:result];
});
But this one won't?
__block NSDictionary *result = nil;
[[SomeService sharedInstance] doSomethingGreatWithReplyBlock:^(NSDictionary * response) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateDisplay:response];
});
}];
Isn't this exactly the same? In the first example I'm waiting for the async operation to finish using a semaphore. Then dispatch_async on the main queue.
In the second one I'm calling dispatch_async (also on the main queue) directly from within the other block (which runs on some background queue). This one still calls the updateDisplay method fine - however it doesn't actually update the UI. It feels like some main thread update issue however [NSThread isMainThread] still returns true...
Is there any obvious difference I'm missing here? I'm pretty lost here and would appreciate any explanation. I have never observed such weird behavior before.
I'm using CoreBluetooth to connect and exchange data with a peripheral device using Bluetooth Low Energy. To connect to my peripheral I'm using the following method (for clarity, manager is an instance of the CBCentralManager class).
- (void)connectPeripheral:(CBPeripheral *)peripheral {
// Connects with the peripheral
[manager connectPeripheral:peripheral options:nil];
}
Now I wish to write the asynchronous version of this method using Grand Central Dispatch, blocks and semaphores. I would like to have a version that times out within a specific time interval. First I defined this method:
void dispatchAsyncWithCompletionAndTimeout(dispatch_queue_t queue, int64_t timeoutInNanoseconds,
dispatch_block_t block, dispatch_block_t completionBlock,
dispatch_block_t timeoutBlock) {
NSCParameterAssert(queue);
NSCParameterAssert(timeoutInNanoseconds >= 0);
NSCParameterAssert(block);
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
}
This function basically contains three blocks: the first is the action block (e.g. connect) and the second and third one are the handler blocks which should be called whether the action code is performed within or after the timeout expiration, respectively.
What I did next is to turn my connectPeripheral: method into an asynchronous method by wrapping it inside the following method:
- (void)connectPeripheralAnsync:(CBPeripheral *)peripheral withinTimeout:(NSInteger)timeout {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatchAsyncWithCompletionAndTimeout(queue, (int64_t)timeout * NSEC_PER_SEC, ^{
[self connectPeripheral:peripheral];
}, ^{
NSLog(#"Peripheral discovered");
}, ^{
NSLog(#"Time runned out");
});
}
Unfortunately my third block never gets called, even if no peripheral could be discovered by the manager until the timeout expires. Where do I fail?
Looks like there's no problems with GCD
i get you code and remade it a little bit to check timeout stuff easy, so changing secsForTask and secsForTimeout I can check what called:
unsigned int secsForTask = 3;
unsigned int secsForTimeout = 2;
dispatch_queue_t queue = dispatch_queue_create("com.test.111", DISPATCH_QUEUE_CONCURRENT);
int64_t timeoutInNanoseconds = secsForTimeout * NSEC_PER_SEC;
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutInNanoseconds);
dispatch_block_t timeoutBlock = ^{
NSLog(#"timeout");
};
dispatch_block_t completionBlock = ^{
NSLog(#"completion");
};
dispatch_block_t block = ^{
NSLog(#"block start");
sleep(secsForTask);
NSLog(#"block end");
};
dispatch_async(queue, ^{
long timedOut = dispatch_semaphore_wait(semaphore, timeoutTime);
if (timedOut) {
if (timeoutBlock) {
timeoutBlock();
}
} else if (completionBlock) {
completionBlock();
}
});
block();
dispatch_semaphore_signal(semaphore);
});
on secsForTask = 3 and secsForTimeout = 2
block start
timeout
block end
on secsForTask = 1 and secsForTimeout = 2
block start
block end
completion
probably you have to check if everything is ok with your timeout, so you don't pass in method connectPeripheralAnsync:withinTimeout: timeout in nanosecs.
-[CBCentralManager connectPeripheral: options:] method is a non-block async method.
so, this method call alway pass through next line, immediately.
in your dispatchAsyncWithCompletionAndTimeout(...) method case.
the block that contains "-[CBCentralManager connectPeripheral: options:]" executed.
and then dispatch_semaphore_signal(semaphore); executed without blocking thread.
so, you always cannot get timeout.
-[CBCentralManager connectPeripheral: options:] call will response with
-[CBCentralDelegate centralManager: didConnect:] when connected successfully.
so, you should approach differently.
dispatch_semaphore_signal(semaphore); should call on -[CBCentralDelegate centralManager: didConnect:].
I have a senario that requires me to make multiple call to a web api. The following is an example.
getDataAsync:(NSDictionary *)dictionary withCompletion: (void (^)(NSDictionary*))completion {
__block int counter = n; // the number of async blocks
__block NSMutableDictionary *output = [[NSMutableDictionary alloc] init];
void (^returnBlock)(void) = ^{
counter--;
if(counter != 0) return;
completion(#{#"return": output});
return;
};
void (^getResourceA)(void) = ^{
[service getResourceA : dictionary[#"idA"] completion:
^(ServiceResult results, MyResourceA *a, NSString *errMsg) {
[output setValue:a.value forKey:a.name];
returnBlock();
}];
};
// followed by n-1 other blocks like getResourceA
//...
}
I want to use the built in dispatch_queue rather than my own custom solution here. How can I do that given the inner completion block used by the asynchronous service call?
Also any other advice on how to go about this would be appreciated.
Dispatch groups have been invented for this purpose:
dispatch_group_t requestGroup = dispatch_group_create();
dispatch_group_async(requestGroup, queue, ^{
// ...
});
dispatch_group_wait(requestGroup, DISPATCH_TIME_FOREVER);
completionBlock();
Or instead of waiting:
dispatch_group_notify(requestGroup, dispatch_get_main_queue(), ^{
completionBlock();
});
Also, instead of dispatching blocks to the group, you can also enter and leave a group manually, which works well with asynchronous service APIs:
dispatch_group_enter(requestGroup);
[service getResourceA : dictionary[#"idA"] completion: ^(ServiceResult results, MyResourceA *a, NSString *errMsg) {
[output setValue:a.value forKey:a.name];
dispatch_group_leave(requestGroup);
}];
Use dispatch_group_t. See Waiting on Groups of Queued Tasks.
The topic doesn't mention it, but use dispatch_group_notify to register a block instead of waiting inline.
I have a series of dispatch_async that I am performing and I would like to only update the UI when they are all done. Problem is the method within dispatch_async calls something in a separate thread so it returns before the data is fully loaded and dispatch_group_notify is called before everything is loaded.
So I introduce a infinite loop to make it wait until a flag is set.
Is this the best way? See code below.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_async(group, queue, ^{
__block BOOL dataLoaded = NO;
[thirdPartyCodeCallWithCompletion:^{
dataLoaded = YES;
}];
// prevent infinite loop
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
queue, ^{
dataLoaded = YES;
});
// infinite loop to wait until data is loaded
while (1) {
if (dataLoaded) break;
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//update UI
});
}
You're already aware of dispatch groups. Why not just use dispatch_group_wait(), which includes support for a timeout? You can use dispatch_group_enter() and dispatch_group_leave() rather than dispatch_group_async() to make the group not done until the internal block for the third-party call with completion is finished.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[thirdPartyCodeCallWithCompletion:^{
dispatch_group_leave(group);
}];
}
}
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSECS_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
//update UI
});
The use of dispatch_group_wait() does make this code synchronous, which is bad if run on the main thread. Depending on what exactly is supposed to happen if it times out, you could use dispatch_group_notify() as you were and use dispatch_after() to just updates the UI rather than trying to pretend the block completed.
Update: I tweaked my code to make sure that "update UI" happens on the main queue, just in case this code isn't already on the main thread.
By the way, I only used dispatch_async() for the block which calls thirdPartyCodeCallWithCompletion: because your original used dispatch_group_async() and I wasn't sure that the hypothetical method was asynchronous. Most APIs which take a completion block are asynchronous, though. If that one is, then you can just invoke it directly.
Another method is to use semaphore and the dispatch_semaphore_wait:
// Create your semaphore, 0 is specifying the initial pool size
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#autoreleasepool {
// Your code goes here
}
// Release the resource and signal the semaphore
dispatch_semaphore_signal(semaphore);
});
// Wait for the above block execution, AKA Waits for (decrements) a semaphore.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// After this line you can now safely assert anything you want regarding the async operation since it is done.
I have a method that I add to a GCD queue that I have created (so it's a serial queue) and then run it async. From within that block of code I make a dispatch to the main queue, when that block of code dispatched to the main queue is complete I set a BOOL flag to YES, so that I further down in my code can check if this condition is YES then I can continue to the next method. Here is the code in short:
dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_async(queue, ^{
Singleton *s = [Singleton sharedInstance];
dispatch_sync(dispatch_get_main_queue(), ^{
[s processWithCompletionBlock:^{
// Process is complete
processComplete = YES;
}];
});
});
while (!processComplete) {
NSLog(#"Waiting");
}
NSLog(#"Ready for next step");
However this does not work, because dispatch_sync is never able to run the code on the main queue. Is this because I'm running a while loop on the main queue (rendering it busy)?
However if I change the implementation of the while loop to this:
while (!processComplete) {
NSLog(#"Waiting")
NSDate *date = [NSDate distantFuture];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date];
}
It works without a glitch. Is this an acceptable solution for this scenario? Can I do it any other preferred way? What kind of magic stuff does NSRunLoop do? I need to understand this better.
Part of the main thread's NSRunLoop job is to run any blocks queued on the main thread. By spinning in a while-loop, you're preventing the runloop from progressing, so the queued blocks are never run unless you explicitly make the loop run yourself.
Runloops are a fundemental part of Cocoa, and the documentation is pretty good, so I'd reccommend reading it.
As a rule, I'd avoid manually invoking the runloop as you're doing. You'll waste memory and make make things complicated very quickly if you have multiple manual invocations running on top of one another.
However, there is a much better way of doing this. Split your method into a -process and a -didProcess method. Start the async operation with your -process method, and when it completes, call -didProcess from the completion block. If you need to pass variables from one method to the other, you can pass them as arguments to your -didProcess method.
Eg:
dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_async(queue, ^{
Singleton *s = [Singleton sharedInstance];
dispatch_sync(dispatch_get_main_queue(), ^{
[s processWithCompletionBlock:^{
[self didProcess];
}];
});
});
You might also consider making your singleton own the dispatch queue and make it responsible for handling the dispatch_async stuff, as it'll save on all those nasty embedded blocks if you're always using it asynchronously.
Eg:
[[Singleton sharedInstance] processAyncWithCompletionBlock:^{
NSLog(#"Ready for next step...");
[self didProcess];
}];
Doing something like what you posted will most likely freeze the UI. Rather than freezing up everything, call your "next step" code in a completion block.
Example:
dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
Singleton *s = [Singleton sharedInstance];
dispatch_async(dispatch_get_main_queue(), ^{
[s processWithCompletionBlock:^{
// Next step code
}];
});
});
Don't go creating a loop like that waiting for a value inside a block, variables in blocks are read only, instead call your completion code from inside the block.
dispatch_async(queue, ^{
Singleton *s = [Singelton sharedInstance];
[s processWithCompletionBlock:^{
//process is complete
dispatch_sync(dispatch_get_main_queue(), ^{
//do something on main queue....
NSLog(#"Ready for next step");
});
}];
});
NSLog(#"waiting");