I tried running the following code and it raises the following error every time:
DispatchQueue.main.sync { }
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
I found this post on stackoverflow that says to never run synchronous code on the main queue:
DispatchQueue crashing with main.sync in Swift
I had assumed that because the sync { } method is there that means there is some context that it can be used. Is there absolutely no use for executing synchronous code on the main queue?
I had assumed that because the sync { } method is there that means there is some context that it can be used.
Yes, it's there to be used when appropriate, but that doesn't mean it should be applied to the main queue.
Is there absolutely no use for executing synchronous code on the main
queue?
The sync command blocks and waits for its operation to be performed and completed on the specified queue. That queue can certainly be the main queue. But the queue that blocks cannot be the main queue! You must never say sync when you are on the main queue, as you will then be blocking the main queue which is illegal; and you must really never say DispatchQueue.main.sync when you are on the main queue, as you will be blocking the main queue forever (thereby causing the heat death of the universe).
Really the best thing to do is adopt async/await and never mention DispatchQueue again. All these concerns vanish in a puff of smoke and your code becomes safe and easy to reason about, automatically.
sync should not be used in the main queue because you are likely to block everything, the method is there for "custom" queues (for example you created a queue and YOU handle when it has to be blocked and unblocked) the main queue is a special case, and since it is not manage by you introducing a block may generate unexpected behavior (that usually translates into a crash)
Most answers on stackoverflow implies in a way that sync vs async behaviour is quite similar to serial vs concurrent queue concept difference. Like the link in the first comment by #Roope
I have started to think that
Serial and concurrent are related to DispatchQueue, and sync/ async for how an operation will get executed on a thread.
Am I right?
Like if we've got DQ.main.sync then task/operation closure will get executed in a synchronous manner on this serial (main) queue.
And, if I do DQ.main.async then task will get asynchronously on some other background queue, and on reaching completion will return control on main thread.
And, since main is a serial queue, it won't let any other task/operation get into execution state/ start getting executed until the current closure task has finished its execution.
Then,
DQ.global().sync would execute a task synchronously on the thread on which its task/operation has been assigned i.e., it will block that thread from doing any other task/operation by blocking any context switching on that particular thread.
And, since, global is a concurrent queue it will keep on putting the tasks present in it to the execution state irrespective of previous task/operation's execution state.
DQ.global().async would allow context switching on the thread on which the operation closure has been put for execution
Is this the correct interpretations of the above dispatchQueues and sync vs async?
You are asking the right questions but I think you got a bit confused (mostly due to not very clear posts about this topic on internet).
Concurrent / Serial
Let's look at how you can create a new dispatch Queue:
let serialQueue = DispatchQueue(label: label)
If you don't specify any other additional parameter, this queue will behave as a serial queue:
This means that every block dispatched on this queue (sync or async it doesn't matter) will be executed alone, without the possibility for other blocks to be executed, on that same queue, simultaneously.
This doesn't mean that anything else is stopped, it just means that if something else is dispatched on that same queue, it will wait for the first block to finish before starting it's execution. Other threads and queues will still run on their own.
You can, however, create a concurrent queue, that will not constraint this blocks of code in this manner and, instead, if it happens that more blocks of code are dispatched on that same queue at the same time, it will execute them at the same time (on different threads)
let concurrentQueue = DispatchQueue(label: label,
qos: .background,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: .global())
So, you just need to pass the attribute concurrent to the queue, and it won't be serial anymore.
(I won't be talking about the other parameters since they are not in focus of this particular question and, I think, you can read about them in the other SO post linked in the comment or, if it's not enough, you can ask another question)
If you want to understand more about concurrent queues (aka: skip if you don't care about concurrent queues)
You could ask: When do I even need a concurrent queue?
Well, just for example, let's think of a use-case where you want to synchronize READS on a shared resource: since the reads can be done simultaneously without issues, you could use a concurrent queue for that.
But what if you want to write on that shared resource?
well, in this case a write needs to act as a "barrier" and during the execution of that write, no other write and no reads can operate on that resource simultaneously.
To obtain this kind of behavior, the swift code would look something like this
concurrentQueue.async(flags: .barrier, execute: { /*your barriered block*/ })
So, in other words, you can make a concurrent queue work temporarily as a serial queue in case you need.
Once again, the concurrent / serial distinction is only valid for blocks dispatched to that same queue, it has nothing to do with other concurrent or serial work that can be done on another thread/queue.
SYNC / ASYNC
This is totally another issue, with virtually no connection to the previous one.
This two ways to dispatch some block of code are relative to the current thread/queue you are at the time of the dispatch call. This dispatch call blocks (in case of sync) or doesn't block (async) the execution of that thread/queue while executing the code you dispatch on the other queue.
So let's say I'm executing a method and in that method I dispatch async something on some other queue (I'm using main queue but it could be any queue):
func someMethod() {
var aString = "1"
DispatchQueue.main.async {
aString = "2"
}
print(aString)
}
What happens is that this block of code is dispatched on another queue and could be executed serially or concurrently on that queue, but that has no correlation to what is happening on the current queue (which is the one on which someMethod is called).
What happens on the current queue is that the code will continue executing and won't wait for that block to be completed before printing that variable.
This means that, very likely, you will see it print 1 and not 2. (More precisely you can't know what will happen first)
If instead you would dispatch it sync, than you would've ALWAYS printed 2 instead of 1, because the current queue would've waited for that block of code to be completed, before continuing in it's execution.
So this will print 2:
func someMethod() {
var aString = "1"
DispatchQueue.main.sync {
aString = "2"
}
print(aString)
}
But does it mean that the queue on which someMethod is called is actually stopped?
Well, it depends on the current queue:
If it's serial, than yes. All the blocks previously dispatched to that queue or that will be dispatched on that queue will have to wait for that block to be completed.
If it's concurrent, than no. All concurrent blocks will continue their execution, only this specific block of execution will be blocked, waiting for this dispatch call to finish it's work. Of course if we are in the barriered case, than it's like for serial queues.
What happens when the currentQueue and the queue on which we dispatch are the same?
Assuming we are on serial queues (which I think will be most of your use-cases)
In case we dispatch sync, than deadlock. Nothing will ever execute on that queue anymore. That's the worst it could happen.
In case we dispatch async, than the code will be executed at the end of all the code already dispatched on that queue (including but not limited to the code executing right now in someMethod)
So be extra careful when you use the sync method, and be sure you are not on that same queue you are dispatching into.
I hope this let you understand better.
I have started to think that Serial and concurrent are related to DispatchQueue, and sync/async for how an operation will get executed on a thread.
In short:
Whether the destination queue is serial or concurrent dictates how that destination queue will behave (namely, can that queue run this closure at the same time as other things that were dispatched to that same queue or not);
Whereas sync vs async dictates how the current thread from which you are dispatching will behave (namely, should the calling thread wait until the dispatched code to finish or not).
So, serial/concurrent affects the destination queue to which you are dispatching, whereas sync/async affects the current thread from which you are dispatching.
You go on to say:
Like if we've got DQ.main.sync then task/operation closure will get executed in a synchronous manner on this serial (main) queue.
I might rephrase this to say “if we've got DQ.main.sync then the current thread will wait for the main queue to perform this closure.”
FWIW, we don’t use DQ.main.sync very often, because 9 times out of 10, we’re just doing this to dispatch some UI update, and there’s generally no need to wait. It’s minor, but we almost always use DQ.main.async. We do use sync is when we’re trying to provide thread-safe interaction with some resource. In that scenario, sync can be very useful. But it often is not required in conjunction with main, but only introduces inefficiencies.
And, if I do DQ.main.async then task will get asynchronously on some other background queue, and on reaching completion will return control on main thread.
No.
When you do DQ.main.async, you’re specifying the closure will run asynchronously on the main queue (the queue to which you dispatched) and that that your current thread (presumably a background thread) doesn’t need to wait for it, but will immediately carry on.
For example, consider a sample network request, whose responses are processed on a background serial queue of the URLSession:
let task = URLSession.shared.dataTask(with: url) { data, _, error in
// parse the response
DispatchQueue.main.async {
// update the UI
}
// do something else
}
task.resume()
So, the parsing happens on this URLSession background thread, it dispatches a UI update to the main thread, and then carries on doing something else on this background thread. The whole purpose of sync vs async is whether the “do something else” has to wait for the “update the UI” to finish or not. In this case, there’s no point to block the current background thread while the main is processing the UI update, so we use async.
Then, DQ.global().sync would execute a task synchronously on the thread on which its task/operation has been assigned i.e., ...
Yes DQ.global().sync says “run this closure on a background queue, but block the current thread until that closure is done.”
Needless to say, in practice, we would never do DQ.global().sync. There’s no point in blocking the current thread waiting for something to run on a global queue. The whole point in dispatching closures to the global queues is so you don’t block the current thread. If you’re considering DQ.global().sync, you might as well just run it on the current thread because you’re blocking it anyway. (In fact, GCD knows that DQ.global().sync doesn’t achieve anything and, as an optimization, will generally run it on the current thread anyway.)
Now if you were going to use async or using some custom queue for some reason, then that might make sense. But there’s generally no point in ever doing DQ.global().sync.
... it will block that thread from doing any other task/operation by blocking any context switching on that particular thread.
No.
The sync doesn’t affect “that thread” (the worker thread of the global queue). The sync affects the current thread from which you dispatched this block of code. Will this current thread wait for the global queue to perform the dispatched code (sync) or not (async)?
And, since, global is a concurrent queue it will keep on putting the tasks present in it to the execution state irrespective of previous task/operation's execution state.
Yes. Again, I might rephrase this: “And, since global is a current queue, this closure will be scheduled to run immediately, regardless of what might already be running on this queue.”
The technical distinction is that when you dispatch something to a concurrent queue, while it generally starts immediately, sometimes it doesn’t. Perhaps all of the cores on your CPU are tied up running something else. Or perhaps you’ve dispatched many blocks and you’ve temporarily exhausted GCD’s very limited number of “worker threads”. Bottom line, while it generally will start immediately, there could always be resource constraints that prevent it from doing so.
But this is a detail: Conceptually, when you dispatch to a global queue, yes, it generally will start running immediately, even if you might have a few other closures that you have dispatched to that queue which haven’t finished yet.
DQ.global().async would allow context switching on the thread on which the operation closure has been put for execution.
I might avoid the phrase “context switching”, as that has a very specific meaning which is probably beyond the scope of this question. If you’re really interested, you can see WWDC 2017 video Modernizing Grand Central Dispatch Usage.
The way I’d describe DQ.global().async is that it simply “allows the current thread to proceed, unblocked, while the global queue performs the dispatched closure.” This is an extremely common technique, often called from the main queue to dispatch some computationally intensive code to some global queue, but not wait for it to finish, leaving the main thread free to process UI events, resulting in more responsive user interface.
At least since iOS 9 the only non-deprecated thread concurrency type for Core Data operations, not running in the main thread, is NSPrivateQueueConcurrencyType.
My problem is now, that I want to change the thread priority for that, like this:
...:(int) neededPriority {
_queue = dispatch_queue_create("Worker", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(_queue,
dispatch_get_global_queue(neededPriority, 0));
Sometimes I need a high priority, sometimes a low. But when I call [_privateContext performBlock:...] I can't pass a parameter with the needed priority, nor has the context any property to set its thread priority.
Therefore my question: is there any way to put the context queue into a certain thread priority?
NSManagedObjectContext instances are assigned queues, not threads. The queue associated with the performBlock routine may be run an arbitrary background thread, so you can't assign a general priority to a context.
Instead, from inside the performBlock block, you can call NSThread.setThreadPriority to change the priority of the thread that is handling that block.
You might want to save the thread's priority before changing it, and reset it at the end of the block.
That is, if we queue the same thing several time there will be no concurrency.
The one we queued first will be executed first.
I mean there is only one main thread right?
I have found a nice answer here:
NSOperationQueue and concurrent vs non-concurrent
So make all added operations serial you can always set:
[[NSOperationQueue mainQueue] setMaxConcurrentOperationCount:1];
And the answer is... YES and NO
when you create a new NSOperation to add to your queue, you can use
- (void)setQueuePriority:(NSOperationQueuePriority)priority
according to the documentation, the queue will use this priority, and other factors as inter dependency to decide what operation will be executed next.
As long as your operations have the same priority and no inter-operation dependencies, they should be executed in the same order you added them, maybe with other, system related operations, inserted between them.
From documentation:
The NSOperationQueue class regulates the execution of a set of NSOperation objects. After being added to a queue, an operation remains in that queue until it is explicitly canceled or finishes executing its task. Operations within the queue (but not yet executing) are themselves organized according to priority levels and inter-operation object dependencies and are executed accordingly. An application may create multiple operation queues and submit operations to any of them.
Inter-operation dependencies provide an absolute execution order for operations, even if those operations are located in different operation queues. An operation object is not considered ready to execute until all of its dependent operations have finished executing. For operations that are ready to execute, the operation queue always executes the one with the highest priority relative to the other ready operations. For details on how to set priority levels and dependencies, see NSOperation Class Reference.
About threads:
Although you typically execute operations by adding them to an operation queue, doing so is not required. It is also possible to execute an operation object manually by calling its start method, but doing so does not guarantee that the operation runs concurrently with the rest of your code. The isConcurrent method of the NSOperation class tells you whether an operation runs synchronously or asynchronously with respect to the thread in which its start method was called. By default, this method returns NO, which means the operation runs synchronously in the calling thread.
When you submit a nonconcurrent operation to an operation queue, the queue itself creates a thread on which to run your operation. Thus, adding a nonconcurrent operation to an operation queue still results in the asynchronous execution of your operation object code.
So, if I understand correctly here will be no concurrency.
In GCD, is there a way to tell if the current queue is concurrent or not?
I'm current attempting to perform a delayed save on some managed object contexts but I need to make sure that the queue the code is currently executed on is thread-safe (in a synchronous queue).
If you actually have to determine whether or not the queue passed in to you is serial or concurrent, you've almost certainly designed things incorrectly. Typically, an API will hide an internal queue as an implementation detail (in your case, your shared object contexts) and then enqueue operations against its internal queue in order to achieve thread safety. When your API takes a block and a queue as parameters, however, then the assumption is that the passed-in block can be safely scheduled (async) against the passed-queue (when, say, an operation is complete) and the rest of the code is factored appropriately.
Yes, assuming you're doing the work in an NSOperation subclass:
[myOperation isConcurrent] //or self, if you're actually in the NSOperation
If you need to ensure some operations are always executed synchronously, you can create a specific operation queue and set its maximum concurrent operations to 1.
NSOperationQueue * synchronousQueue = [[NSOperationQueue alloc] init];
[synchronousQueue setMaxConcurrentOperationCount:1];
GCD takes some planning ahead. The only other way I can think of is to observe the value isExecuting (or similar) on your NSOperation objects. Check out this reference on that. This solution would be more involved, so I hope the other one works for you.