I read comment on SO that dispatching a queue to the main thread is not the same as performing code on the main thread. If I understood correctly the user was saying that this
dispatch_async(dispatch_get_main_queue(),
^{
// some code
});
was not the same as this
[self performSelectorOnMainThread:@selector(doStuff)
withObject:nil waitUntilDone:NO];
- (void) doStuff {
// some code
}
is there some true about this comment?
Excluding the fact that the first code is asynchronous, for me, both codes are performed equally on the main thread. Is there any technical difference between them?
I am asking that because I had some code to update the UI using dispatch_async on the main thread and it was not working but when I changed that to the second form using performSelectorOnMainThread, it worked.
GCD tends to be simpler to work with for simple tasks you just need to execute and forget. Operations provide much more functionality when you need to keep track of a job or maintain the ability to cancel it. If you're just working with methods or chunks of code that need to be executed, GCD is a fitting choice.
Generally queues are used as a way to 'connect' producers (of data) & consumers (of data). A thread pool is a pool of threads that do some sort of processing. A thread pool will normally have some sort of thread-safe queue (refer message queue) attached to allow you to queue up jobs to be done.
A thread is a sequence of instructions that can be executed by a runtime. Each process has at least one thread. In iOS, the primary thread on which the process is started is commonly referred to as the main thread. This is the thread in which all UI elements are created and managed.
The main thread is the one that starts our program, and it's also the one where all our UI work must happen. However, there is also a main queue, and although sometimes we use the terms “main thread” and “main queue” interchangeably, they aren't quite the same thing.
Yes, there's a difference. The main dispatch queue is a serial queue. That means that, while it's running a task that's been submitted to it, it can't run any other tasks. That's true even if it runs an inner event loop.
-performSelectorOnMainThread:...
operates through a run loop source. Run loop sources can fire in an inner run loop even if that inner run loop is a result of a previous firing of that same source.
One case where this bit me is with running a modal file open dialog. (Non-sandboxed, so the dialog is in-process.) I initiated the modal dialog from a task submitted to the main dispatch queue. It turns out that the internal implementation of the open dialog also dispatches some work to the main queue asynchronously. Since the main dispatch queue was occupied by my task which was running the dialog, it didn't process the framework's tasks until after the dialog completed. The symptom was that the dialog would fail to show the files until some internal timeout had expired, which was on the order of a minute or so.
Note that this wasn't a case of deadlock caused by a synchronous request to the main queue from the main thread, although that can also happen. With GCD, such a synchronous request is certain to deadlock. With -performSelectorOnMainThread:...
, it won't because a synchronous request (waitUntilDone
set to YES
) is just run directly.
By the way, you say "the first code is asynchronous" as if to contrast with the second code. Both are asynchronous, since you passed NO
for waitUntilDone
in the second.
Update:
Consider code like this:
dispatch_async(dispatch_get_main_queue(), ^{
printf("outer task, milestone 1\n");
dispatch_async(dispatch_get_main_queue(), ^{
printf("inner task\n");
});
// Although running the run loop directly like this is uncommon, this simulates what
// happens if you do something like run a modal dialog or call -[NSTask waitUntilExit].
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("outer task, milestone 2\n");
});
This will log:
outer task, milestone 1
outer task, milestone 2
inner task
The inner task won't get to run until the outer task has completed. That's true even though the outer task ran the main run loop, which is what processes tasks dispatched to the main queue. The reason is that the main queue is a serial queue and will never start a new task while it's still running a task.
If you change the inner dispatch_async()
to dispatch_sync()
, then the program will deadlock.
By contrast, consider:
- (void) task2
{
printf("task2\n");
}
- (void) task1
{
printf("task1 milestone 1\n");
[self performSelectorOnMainThread:@selector(task2) withObject:nil waitUntilDone:NO];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("task1 milestone 2\n");
}
(... in some other method:)
[self performSelectorOnMainThread:@selector(task1) withObject:nil waitUntilDone:NO];
That will log:
task1 milestone 1
task2
task1 milestone 2
Running the run loop inside of -task1
gives an opportunity for the inner -performSelectorOnMainThread:...
to run. This is the big difference between the two techniques.
If you change the NO
to YES
in -task1
, this still works without deadlock. That's another difference. That's because, when -performSelectorOnMainThread:...
is called with waitUntilDone
set as true, it checks if it's called on the main thread. If it is, then it just directly invokes the selector right there. It's as though it were just a call to -performSelector:withObject:
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With