Difference between dispatch_async and dispatch_sync on serial queue?
Yes. Using serial queue ensure the serial execution of tasks. The only difference is that dispatch_sync
only return after the block is finished whereas dispatch_async
return after it is added to the queue and may not finished.
for this code
dispatch_async(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_async(_serialQueue, ^{ printf("3"); });
printf("4");
It may print 2413
or 2143
or 1234
but 1
always before 3
for this code
dispatch_sync(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_sync(_serialQueue, ^{ printf("3"); });
printf("4");
it always print 1234
Note: For first code, it won't print 1324
. Because printf("3")
is dispatched after printf("2")
is executed. And a task can only be executed after it is dispatched.
The execution time of the tasks doesn't change anything. This code always print 12
dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
dispatch_async(_serialQueue, ^{ printf("2"); });
What may happened is
- Thread 1: dispatch_async a time consuming task (task 1) to serial queue
- Thread 2: start executing task 1
- Thread 1: dispatch_async another task (task 2) to serial queue
- Thread 2: task 1 finished. start executing task 2
- Thread 2: task 2 finished.
and you always see 12
dispatch_async vs. dispatch_sync using Serial Queues in Grand Central Dispatch
dispatch_async()
means that the block is enqueued and dispatch_async()
returns to enqueueing another task/block (possibly) prior to the block being executed.
With dispatch_sync()
, the block is enqueued and the function will not continue enqueueing another task/block until the block is executed.
The blocks are still executed serially. You could execute 100 dispatch_async()
calls, each with a block that sleeps for 100 seconds, and it'd be really fast. Follow that with a call to dispatch_sync()
on the same serial queue and dispatch_sync()
will return ~10,000 seconds later.
To put it more simply:
dispatch_async(serialQ, block1);
dispatch_async(serialQ, block2);
dispatch_sync(serialQ, block3);
block1
will be executed before block2
which will be executed before block3
. That is the order guaranteed by the serial queue.
However, the calls to dispatch_async()
may return before any of the blocks start executing. The dispatch_sync()
will not return before all three blocks are executed!
Serial Queue with dispatch_sync and dispatch_async
First of all: You should (re-?)read an introduction to GCD. Trying some options until one runs is no option – what you obviously did –, because this can fail on the next machine.
You create a serial queue. In a serial queue one block is executed after each other. This describes the relation of subscripted blocks to each other, not to the caller.
Subscribing synchronously or asynchronously describes the relation between the block and the code subscribing the block. This does not describe the relationships of blocks to each other, but the relationship to the "caller": Something completely different.
Next: Testing for a thread is meaningless. A queue can change the thread it uses. That's what they are for.
To your Q:
If you subscribe a block to a serial queue inside a serial queue, the inner one has to wait for the outer one to be completed, because it is a serial queue. That's the nature of a serial queue. If you do that with dispatch_sync()
the caller waits to, until the block is completed. Therefore it is never completed: Dead lock. Let's have a simplified version of your code:
dispatch_sync(self.serialQueue, // the caller waits for the code to be completed
^{
…
dispatch_sync(self.serialQueue, // the outer block waits for the code to be completed
^{
… // this code runs, after the outer block is completed.
});
…
});
The inner block cannot complete, because it has to wait, before the outer block is completed (serial queue). The outer block cannot complete, because it waits for the inner block to be completed (sync). Dead lock: Both are waiting for the other to be completed.
You want something completely different: You want to use the result of a block. Simply pass the code dealing with the result in a completion handler. Then you can return immediately using the result in a completion handler:
- (void)someStringWithCompletionHandler:(void(^)(NSString *result))handler // No return type, but a completion handler
{
__block NSString *localSomeString;
dispatch_async(self.serialQueue,
^{
if ([NSThread isMainThread]) {
NSLog(@"main thread");
} else {
NSLog(@"background thread");
}
localSomeString = @"fuck you!";
handler( localSomeString );
});
}
And then call it this way:
- (void)goToNext
{
dispatch_async(self.serialQueue,
^{
[self someStringWithCompletionHandler:
^(NSSTring *result)
{
NSLog( @"%@", result );
}]);
});
}
Typed in Safari.
BTW: Mark points in code with comments, not with inline markers. Otherwise none can copy and paste the code and let it run.
Why we need dispatch_sync() on another queue instead of using the current queue/thread in iOS GCD
There could be many reasons why you would want to do this, but one common use case is to guard a critical section of code. Say you have multiple threads that want to update an array. Arrays aren't thread safe so this could lead to the array becoming corrupted.
By using a dispatch_sync
on to a serial queue and updating the array inside the dispatched block you can ensure that only one thread updates the array at a time. You need a synchronous dispatch because you want the requesting thread to wait until the array has been updated before continuing.
For example, here is a simple queue class that uses a serial dispatch queue to ensure thread safety when the underlying array is updated:
class Queue<T> {
private var theQueue = [T]()
private var dispatchQ = dispatch_queue_create("queueQueue", DISPATCH_QUEUE_SERIAL);
func enqueue(object:T) {
dispatch_sync(self.dispatchQ) {
self.theQueue.append(object)
}
}
func dequeue() -> T? {
return self.dequeue(true)
}
func peek() -> T? {
return self.dequeue(false)
}
private func dequeue(remove: Bool) -> T? {
var returnObject: T?
dispatch_sync(self.dispatchQ) {
if !self.theQueue.isEmpty {
returnObject = self.theQueue.first
if (remove) {
self.theQueue.removeFirst()
}
}
}
return returnObject
}
func isEmpty() -> Bool {
return self.theQueue.isEmpty
}
}
Dispatch_barrier_async and serial queue in GCD, what're differences between them?
dispatch_barrier_[a]sync
are meant to be used with a concurrent queue. They are also meant to be used along with calls to dispatch_[a]sync
.
The common usage is the "multi-readers, one writer" pattern. You setup a concurrent queue. For "reader" blocks, you use dispatch_[a]sync
. For "writer" blocks, you use dispatch_barrier_[a]sync
.
This setup allows concurrent reading but only allows one writer at a time and no reading while the writing is happening.
Compare this with a serial queue where only one block at a time can ever happen.
What is the difference between dispatch_sync and dispatch_main?
You don't generally want to use dispatch_main()
. It's for things other than regular applications (system daemons and such). It is, in fact, guaranteed to break your program if you call it in a regular app.
dispatch_sync
runs a block on a queue and waits for it to finish, dispatch_async
runs a block on a queue and doesn't wait for it to finish.
Serial queues run one block at a time, in order. Concurrent queues run multiple blocks at a time, and therefore aren't necessarily in order.
(edit)
Perhaps when you said dispatch_main()
you were thinking of dispatch_get_main_queue()
?
Forcing the order of execution using dispatch_sync
It feels like you're trying to take an existing completion-handler based API and shoe-horn it into a serial queue pattern. But serial queues only make sense when the dispatched blocks are, themselves, synchronous. (And don't confuse this with dispatch_sync
... we're talking about whether the task inside the block is asynchronous or not, not how you dispatched it to your queue.) But you're dealing with asynchronous methods being called from other asynchronous methods.
The basic pattern when dealing with methods that take completion handler blocks, is to eliminate the serial queue altogether (no GCD queue is needed or is useful when the tasks are already asynchronous) and just use completion handler blocks in your own methods, and make the last call in the most deeply nested block call your own completion handler.
For example:
- (void)foo:(void (^)())completion {
[fileObj setFileInfo:file completion:^(NSError *error) {
// do some other stuff
completion();
}];
}
- (void)bar:(void (^)())completion {
[fileObj getFileInfo:fileName completion:^(NSError *error) {
// do some other stuff
completion();
}];
}
And then you'd call it like so:
[self foo:^{
[self bar:^{
// do whatever you want when its all done
}];
}];
This means that it won't do any of the getFile
stuff until the setFile
stuff is done.
Having said this, I wonder what added value your setFile
and getFile
methods add above and beyond the FileObject
classes' own methods.
But the key is that when dealing with asynchronous methods, you can't easily coordinate them with a simple serial GCD queue. If you want dispatch queue like behavior for tasks that are, themselves, asynchronous, then you'd generally consider using operation queues instead (or promises/futures or something like that).
what is the difference between dispatch_async_f and dispatch_async?
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.
Understanding dispatch_queues and synchronous/asynchronous dispatch
This feels like an overly complicated way of thinking of it and there are lots of little details of that description that aren't quite right. Specifically, "it happens immediately on the current thread" is not correct.
First, let's step back: The distinction between dispatch_async
and dispatch_sync
is merely whether the current thread waits for it or not. But when you dispatch something to a serial queue, you should always imagine that it's running on a separate worker thread of GCD's own choosing. Yes, as an optimization, sometimes dispatch_sync
will use the current thread, but you are in no way guaranteed of this fact.
Second, when you discuss dispatch_sync
, you say something about it running "immediately". But it's by no means assured to be immediate. If a thread does dispatch_sync
to some serial queue, then that thread will block until (a) any block currently running on on that serial queue finish; (b) any other queued blocks for that serial queue run and complete; and (c) obviously, the block that thread A itself dispatched runs and completes.
Now, when you use a serial queue for synchronization for, some thread-safe access to some object in memory, often that synchronization process is very quick, so the waiting thread will generally be blocked for a negligible amount of time as its dispatched block (and any prior dispatched blocks) to finish. But in general, it's misleading to say that it will run immediately. (If it always could run immediately, then you wouldn't need a queue to synchronize access).
Now your question talks about a "critical region", to which I assume you're talking about some bit of code that, in order to ensure thread-safety or for some other reason like that, must be synchronized. So, when running this code to be synchronized, the only question re dispatch_sync
vs dispatch_async
is whether the current thread must wait. A common pattern, for example, is to say that one may dispatch_async
writes to some model (because there's no need to wait for the model to update before proceeding), but dispatch_sync
reads from some model (because you obviously don't want to proceed until the read value is returned).
A further optimization of that sync/async pattern is the reader-writer pattern, where concurrent reads are permissible but concurrent writes are not. Thus, you'll use a concurrent queue, dispatch_barrier_async
the writes (achieving serial-like behavior for the writes), but dispatch_sync
the reads (enjoying concurrent performance with respect to other read operations).
dispatch_Async serial queue
Although the interleaving of the operations may not be known, you can see that there are two streams. The first is logging from a serial queue, the other is just plain NSLog.
What you can say about the order is that 1
will precede 3
and 2
will precede 4
Related Topics
Getting Device Orientation in Swift
How to Set Image for Bar Button with Swift
Uicollectionview's Cell Disappearing
Set Dimensions for Uiimagepickercontroller "Move and Scale" Cropbox
Remove Text from Back Button Keeping the Icon
Autolayout Is Ignored in Custom Uitableviewcell
Saving Image to Documents Directory and Retrieving for Email Attachment
Uilabel Wrong Word Wrap in iOS 11
Implement Pushkit and Test in Development Behavior
Choosing the Right iOS Xml Parser
Save Images with Phimagemanager to Custom Album
How to Put an Image in a Realm Database
How to Implement Lazy Loading of Images in Table View Using Swift
Simple and Clean Way to Convert JSON String to Object in Swift
Should I Declare Variables in Interface or Using Property in Objective-C Arc