what is the difference between dispatch_async_f and dispatch_async? - ios

Whats the difference between
dispatch_async_f
and
dispatch_async
in ios?

The main reason behind using this async blocks is to have the background task.
dispatch_async:
By using this block you can run a code block asynchronously
Eg.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates can be done only on main thread
});
});
dispatch_async_f:
Irrespective of the block in async task you can put your custom function to be performed in the background.
Eg:
void mainFunc(void) {} // your function
void callingFuncForAsyncTask(void*) { mainFunc(); } // new function which takes arguments for calling inside async_f
dispatch_async_f(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 0, &callingFuncForAsyncTask);
For more info and parameter details kindly refer:
https://developer.apple.com/reference/dispatch/1452834-dispatch_async_f

dispatch_async -
Submits a block for asynchronous execution on a dispatch queue and returns immediately.
This function is the fundamental mechanism for submitting blocks to a dispatch queue. Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked.
Declaration : void dispatch_async( dispatch_queue_t queue, dispatch_block_t block);
Params :
queue - the queue on which block is to be submitted & can’t be NULL.
block - block to be submitted to the target queue & can’t be NULL.
dispatch_async_f -
Submits a application defined block for async execution on a dispatch queue & returns immediately.
This function is the fundamental mechanism for submitting application-defined functions to a dispatch queue. Calls to this function always return immediately after the function has been submitted and never wait for it to be invoked.
Declaration : void dispatch_async_f( dispatch_queue_t queue, void *context, dispatch_function_t work);
Params :
queue - the queue on which block is to be submitted & can’t be NULL.
work - application defined function to be invoked on target dispatch queue 7 can’t be NULL.

Related

Dispatch task on main queue sync from custom serial queue

I have come across a very interesting problem related to queue dead lock in iOS. Any way to avoid this?
Consider this:
Create a custom serial queue.
Dispatch some task (#1) on this serial queue asynchronously.
This async task (#1) on dispatches some task (#2) onto main queue sync.
Main queue dispatches some task (#3) onto serial queue sync.
Result - DeadLock
Below is the sample code for this.
Since self.opQueue is a serial queue, task#3 will not start till task#1 completes.
Since task#1 is calling main queue sync, so it will never complete till main queue completes task#2.
Since main queue is waiting for opQueue to finish task#3, and opQueue is waiting for main queue to finish task#2 there is a deadlock.
#import "ViewController.h"
#interface ViewController ()
#property(nonatomic,strong) dispatch_queue_t opQueue;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.tarun.sqqueue",
DISPATCH_QUEUE_SERIAL);
self.opQueue = queue;
[self performOperations];
}
/// 1. Dispatch on serial queue async.
/// 2. This async task on serial queue dispatchs some task onto
/// main queue sync.
/// 3. Main queue dispatched some task onto serial queue sync.
/// 4. Result - DeadLock
- (void)performOperations {
/// task#1: Dispatch task on the serial Queue Asynchronously.
/// So this is not blocking.
dispatch_async(self.opQueue, ^{
for (int i = 1; i<=100; i++) {
NSLog(#"%d. Async on Serial Queue from Main Queue.",i);
}
/// task#2: Dispatching task on main queue synchronously from serial
/// queue.So this queue will wait till main queue executes this task.(Blocking)
dispatch_sync(dispatch_get_main_queue(), ^{
for (int i = 1; i<=100; i++) {
NSLog(#"%d. Sync on main queue from Serial Queue.",i);
}
});
});
/// task#3: Dispatching task on swrial queue synchronously from main
/// queue.So main queue will wait till serial queue executes this task. (Blocking)
dispatch_sync(self.opQueue, ^{
for (int i = 1; i<=100; i++) {
NSLog(#"%d. Sync on Serial Queue From Main Queue.",i);
}
});
/// Since self.opQueue is a serial queue, task#3 will not start till task#1 completes.
/// Since task#1 is calling main queue sync,
/// so it will never complete till main queue completes task#2.
/// Since main queue is waiting for opQueue to finish task#3, and opQueue is waiting for main queue to finish task#2 there is a deadlock.
NSLog(#"Back to main queue");
}
#end
From Apple
Important: You should never call the dispatch_sync or dispatch_sync_f
function from a task that is executing in the same queue that you are
planning to pass to the function. This is particularly important for
serial queues, which are guaranteed to deadlock, but should also be
avoided for concurrent queues.
Its a guaranteed dead lock. The only way to avoid this is to not implement it this way.

Get main queue block not getting executed on main thread

I call dispatch_async(dispatch_get_main_queue(),block()) and my block() fails to perform UI interactions, because it IS not getting called in main thread, why?
Note: I use PSPDFUIKitMainThreadGuard
You have to call UI modification blocks as dispatch_sync() on the main thread, as this, it can refresh UI without being blocked.
Here is how you can do this in Swift:
runThisInMainThread { () -> Void in
// Run the method that crashes in here
}
func runThisInMainThread(block: dispatch_block_t) {
dispatch_async(dispatch_get_main_queue(), block)
}
Its included as a standard function in my repo, check it out: https://github.com/goktugyil/EZSwiftExtensions
You are not actually dispatching to the main thread, from the looks of your question, you are invoking your block and passing the return value of your block, e.g. dispatch_async(queue, yourReturnValue);
It should read:
dispatch_async(queue, yourBlock);
Not:
dispatch_async(queue, yourBlock());

Implementing Critical section using GCD in iOS

I have a really heavy task to perform, and I dont want to block the main thread. So I'm starting a separate Concurrent queue for it. There can be 4 instances of the said task.
-(dispatch_queue_t)getConcurrentQueue
{
if(concurrentQueue == nil)
{
concurrentQueue = dispatch_queue_create("com.myself.HeavyTask", DISPATCH_QUEUE_CONCURRENT);
}
return concurrentQueue;
}
Now to start the heavy task I have -
-(void)beginTask
{
//.....
//.....
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = [self getConcurrentQueue];
dispatch_async(queue, ^{
[weakSelf heavyTask];
});
}
Now the method heavyTask goes like this --
-(void)heavyTask
{
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Initialising code for heavy task
// This is the critical section. Only one of the 4 concurrent threads can enter this at a time
}
//....
while(condition)
{
// Perform meat of the task
}
//...
dispatch_sync(dispatch_get_current_queue(), ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
}
//...
}
The initialising code and teardown code uses some 3rd party C methods which are not thread safe. So making them thread safe is not the scope of the question.
Now I've put the "initialising code" and and the "teardown code" within -
dispatch_sync(dispatch_get_current_queue(), ^{
}
My code is crashing and I'm getting error messages stating that there is insufficient thread locking around the critical section code.
I read that dispatch_get_current_queue() is not safe, so I replaced it with concurrentQueue. I also tried replacing with dispatch_get_main_queue(). Still the code crashes, complaining about insufficient thread locking.
I know there is something wrong in my understanding of implementing critical section using GCD.
Can anyone clearly show me how to make my code work properly here??
Side question -- Can I use #synchronized { } blocks here??
There's a lot wrong with your code, including not adhering to naming conventions.
So basically, if you want to execute the same task concurrently with respect to each other, use the global concurrent queue to execute those tasks.
If you want to concurrently access shared resources from within these tasks (or from elsewhere), define a dedicated queue, say "sync_queue" where you exclusively access these resources. This "sync_queue" executes your "critical sections".
The "sync_queue" can be serial or concurrent.
If you use a serial queue, use dispatch_async(sync_queue, block) for write access and dispatch_sync(sync_queue, block) for read access to shared resources.
If you use a concurrent queue, use dispatch_barrier_async(sync_queue, block) for write access and dispatch_barrier_sync(sync_queue, block) for read access to shared resources.
Example:
// Read access using a serial sync_queue:
...
__block int counter;
dispatch_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a serial sync_queue:
...
dispatch_async(sync_queue, ^{
_counter = counter;
});
// Read access using a concurrent sync_queue:
...
__block int counter;
dispatch_barrier_sync(sync_queue, ^{
counter = _counter;
});
// Write access using a concurrent sync_queue:
...
dispatch_barrier_async(sync_queue, ^{
_counter = counter;
});
Example for your "heavy task":
-(void)heavyTask
{
dispatch_barrier_async(sync_queue, ^{
// Initialize heavy task
...
// Continue with the task:
dispatch_async(dispatch_get_global_queue(0,0), ^{
BOOL condition = YES; // condition must be local to the block (it's not a shared resource!)
while(condition)
{
// Perform meat of the task
condition = ...;
}
dispatch_barrier_async(sync_queue, ^{
// Teardown code. Freeing memory etc.
// This is also a critical section.
...
}
});
}
}
You called it "getSerialQueue" but really you are creating a "concurrent" queue in it. Try to fix it substituting DISPATCH_QUEUE_CONCURRENT with DISPATCH_QUEUE_SERIAL in getSerialQueue.
Keep in mind that:
dispatch_sync means: I will wait here until this block finishes
dispatch_async means: I will not wait
This is not related to concurrent or serial. If two tasks in a concurrent queue call dispatch_sync(block), 'block' will be executed concurrently.
Hope this helps.

How to wait till some actions completed when its invoking Async APIs

We have some set of actions but, each actions invokes Async APIs. We want wait till Async API got back and then start execution of second action.
Ex: We have X, Y and Z actions: Method1 does X action and method2 does Y action and Method3 does Z action. Here Method1 internally calls some Async API. So we don't want invoke Method2 until Method1 complete.
method1 ()
// Here wait till method1 complete
method2 ()
// Here wait till method12 complete
method3 ()
method 1
{
block{
// This block will be called by Async API
};
// here invoking Async API
}
What can be used to wait till method1 complete. Which mechanism of Objective-C is more efficient?
Thanks in advance
Just invoke your methods in the main thread, because Async API processes in the background thread.
You can use a dispatch_semaphore_t, which you signal at the end of your blocks (the asynchronous completion blocks). Furthermore, if method1, method2, method3 always get called in sequence then they need to share the semaphore and I would move the whole thing to as single method. Here is an example of how it can be done:
- (void) method123
{
dispatch_semaphore_t waitSema = dispatch_semaphore_create(0);
callAsynchAPIPart1WithCompletionBlock(^(void) {
// Do your completion then signal the semaphore
dispatch_semaphore_signal(waitSema);
});
// Now we wait until semaphore is signaled.
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
callAsynchAPIPart2WithCompletionBlock(^(void) {
// Do your completion then signal the semaphore
dispatch_semaphore_signal(waitSema);
});
// Now we wait until semaphore is signaled.
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
callAsynchAPIPart3WithCompletionBlock(^(void) {
// Do your completion then signal the semaphore
dispatch_semaphore_signal(waitSema);
});
// Now we wait until semaphore is signaled.
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
dispatch_release(waitSema);
}
Alternately, you can chain your calls through your completion blocks as follows:
- (void) method123
{
callAsynchAPIPart1WithCompletionBlock(^(void) {
// Do completion of method1 then call method2
callAsynchAPIPart2WithCompletionBlock(^(void) {
// Do completion of method2 then call method3
callAsynchAPIPart1WithCompletionBlock(^(void) {
// Do your completion
});
});
});
}

Ensuring the codes in different sections run in the background queue in iOS

I am new to multithreading in iOS. I need to do three things: get information from the api, parse the information and save to my database. I have these three things in a different files(getAPI,parseAPI and savetoDB). getAPI will call parseAPI and it will in return call savetoDB. I want all three of them to work in background thread.
My question is when I call getAPI, will parseAPI and savetoDB run in the background thread as well? How do I ensure that all three of them run in the background? How do I return the call back to main thread after savetoDB?
Example:
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("lakesh", NULL);
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
});
}
Need some guidance.. Thanks...
If you issue a function on a background thread, all execution will continue on that thread until it finishes or you call back another function on the main thread. I had worries like you in the beginning, so I made myself the following macros:
/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
NSAssert(![NSThread isMainThread], #"Don't block the UI thread please!")
/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
NSAssert([NSThread isMainThread], #"You aren't running in the UI thread!")
As you can see by the comments, I tend to use these macros at the beginning of methods I want to make sure I'm not using by error in the wrong thread. I've put these macros and more random stuff at https://github.com/gradha/ELHASO-iOS-snippets which you may find useful.
With regards to your question on returning to the main thread, since you are using GCD the best would be to call dispatch_get_main_queue() at the end of your savetoDB with the code you want to run there. If savetoDB is a library function, its entry point should allow passing in the success block you want to run on the main thread when everything finished. This is the pattern used by libraries like https://github.com/AFNetworking/AFNetworking. Note how their examples provide an API where stuff runs in the background and then your code gets called back (usually in the main thread).
Yes, parseAPI and savetoDB will run in the new queue you have created. If you need to modify the UI when the operations are finished, that code must run in the main thread. To do that, get a reference to the main queue and send it some code. For example:
- (void)startprocess {
dispatch_async(backgroundQueue, ^(void) {
[self getAPI];
dispatch_async(dispatch_get_main_queue(), ^{
// Refresh the UI with the new information
});
});
}
Don't forget to dispatch_release your new queue when you're done with it.
Another pattern, used by Cocoa itself in many parts of the framework, is to add callback block to the signatures of your API functions that is invoked when the background operation has ended. This Stack Overflow thread explains how to do that.
Yes of course if getAPI calls parseAPI, the code of parseAPI will execute on the same thread than the one getAPI was executed, so in your example on a background queue.
To return the callback to the main thread at the end, use the same techniques as Apple uses with their completionBlock you can see on multiple Apple APIs : simply pass a block (e.g. dispatch_block_t or void(^)(NSError*) or whatever fits your needs) as a parameter to your getAPI: method which will pass it to parseAPI: which will in turn pass it to savetoDB: and at the end savetoDB: can simply use dipatch_async(dispatch_get_main_queue, completionBlock); to call this block of code (passed from method to method) on the main thread.
Note: for your getAPI you can use Apple's sendAsynchronousRequest:queue:completionHandler: method, that will automatically execute the request in the background then call the completion block on the indicated NSOperationQueue (NSOperationQueue uses GCD's dispatch_queue internally). See documentation on NSOperationQueue, GCD and the Concurrency Programming Guide and all the great detailed guide in Apple doc for more info.
-(void)getAPI:( void(^)(NSError*) )completionBlock
{
NSURLRequest* req = ...
NSOperationQueue* queue = [[NSOperationQueue alloc] init]; // the completionHandler will execute on this background queue once the network request is done
[NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse* resp, NSData* data, NSError* error)
{
if (error) {
// Error occurred, call completionBlock with error on main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... parseAPI:data completion:completionBlock];
}
}];
}
-(void)parseAPI:(NSData*)dataToParse completion:( void(^)(NSError*) )completionBlock
{
... parse datatToParse ...
if (parsingError) {
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
} else {
[... savetoDB:dataToSave completion:completionBlock];
}
}
-(void)savetoDB:(id)dataToSave completion:( void(^)(NSError*) )completionBlock
{
... save to your DB ...
// Then call the completionBlock on main queue / main thread
dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(dbError); }); // dbError may be nil if no error occurred of course, that will tell you everything worked fine
}
-(void)test
{
[... getAPI:^(NSError* err)
{
// this code will be called on the main queue (main thread)
// err will be nil if everythg went OK and vontain the error otherwise
}];
}

Resources