On iOS device, I recently found that a strange behavior.
Code1:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"1111");
});
while (1) {
sleep(1);
}
});
Code2:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"1111");
});
while (1) {
sleep(0.5);
}
});
Code1 and Code2's only difference is that Code1 sleep 1second every loop and Code2 sleep 0.5.
If you run these two code on iOS device with single core, Code1 will print out the #"1111", but Code2 won't.
I don't why, the global queue is assumed to be concurrent.It should always print out the number no matter what other blocks are doing. And if it is something due to that single core device's limit, why sleep(0.5) and sleep(1) would make the difference?
I really want to know the reason for this.
EDIT
I found use sleep(0.5) is my stupid mistake. sleep() function take an unsigned int parameter.So sleep(0.5) is equal to sleep(0). But do sleep(0) will block the whole concurrent queue?
The reason is that your second sleep() is essentially a sleep(0) which means that you're now buzz-looping the thread that GCD gave to you, and that's probably the same thread that would have executed the nested dispatch_async() if you had given it a chance to do anything else, which the first example does. During the one second sleep, GCD sees that the thread is blocked and creates a new one to service the outstanding queued request(s). In the second example, you're essentially computationally starving the enqueued work - GCD is not smart enough to know that a thread has been locked into an infinite loop, and you're not giving the system enough work to justify (in GCD's eyes) the creation of another thread, so... You've essentially discovered a bug in GCD's low-threshold of work logic, I think.
Just checked out, 1st and 2nd snippets print "1111".
Note, nesting of dispatch_async you use won't give any profit, because you set the same priorities (DISPATCH_QUEUE_PRIORITY_DEFAULT) all the tasks "NSLog(#"1111");"
and "
while (1) {
sleep(0.5);
"
will be added to the same target queue. As the result I can assume that in the first case block with WHILE will be executed first, and because it will not finish never, the next task in the queue(NSLog(...)) will be never called.
You can try to use different priorities for the queues (DISPATCH_QUEUE_PRIORITY_LOW f.e.).
Related
I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:#selector(doit:) withObject:YES waitUntilDone:YES];
Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.
As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.
I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.
When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:
Calling this function and targeting
the current queue results in deadlock.
where for -performSelectorOnMainThread: we see
wait
A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.
If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.
I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.
I then use
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.
performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.
Here's how the documentation says the method is implemented:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
And on performSelector:target:withObject:order:modes:, the documentation states:
This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.
I've inherited a codebase that's using the following structure for threading:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
//Several AFNetworking Server calls...
})
})
I'm not very experienced with threading, so I'm trying to figure out what the possible intention behind this structure. Why grab the main queue only to access another queue immediately? Is this a common practice? For a little more context, this code is executed in an UIApplicationDidBecomeActiveNotification notification, making several necessary service calls.
Is this structure safe? Essentially my goal is to make the service calls without blocking the UI. Any help or input is appreciated.
So I think this is an interesting few lines that somebody decided to write, so let's break down what's happening here (I may be breaking things down too much, sorry in advance, it just helps my own train of thought)
dispatch_async(dispatch_get_main_queue(), dispatch_block_t block)
This will put the block as a task on the main queue (which you the code is already running in), then immediately continue executing the code in the rest of the method (If he had wanted to wait for the block task to finish before continuing, he'd have made a dispatch_sync call instead).
The main queue is serial, so it will perform these tasks exactly in this order:
go ahead and execute the block after the end of the current method (the end of the run loop for the current task)
execute any other tasks that may have been asynchronously added to the main queue before you dispatch_async your block task into the queue
execute the block task
Now block just dispatches another task to the high priority global queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block2)
The DISPATCH_QUEUE_PRIORITY_HIGH is a concurrent queue-- so if you were to dispatch multiple tasks to this queue, it could potentially do them in parallel, depending on several system factors.
Your old co-worker wanted to make sure the networking calls in block2 were done ASAP
Because block is calling dispatch_async (which returns immediately), block task finishes, allowing the main queue to execute the next task in the queue.
The net result so far is that block2 is queued into the high priority global queue. After it executes, and your network calls complete, callback methods will be called and yadayada
...So what is the order of what's happening?
dispatch_async(dispatch_get_main_queue(), { () -> Void in
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
//Several AFNetworking Server calls...
})
})
//moreCode
1) moreCode executes
2) block executes (adds block2 with network calls onto global queue)
3/4) Next task in main queue executes
4/3) Network task in global queue executes
The order of which would happen first may vary between 3 and 4, but that's concurrency for you :)
So unless old coworker wanted moreCode to execute first before adding the network calls to a global queue, you can go ahead and remove that initial dispatch_async into the main queue.
Assuming it looks like they wanted the network calls done ASAP, there probably is no reason to delay the addition of those networking tasks into a global queue.
Open to any input ^^. My experience involves reading all of the documentation on GCD today, then deciding to look at some GCD tagged questions
I am having UI frozen/blocked due to semaphore_wait_trap in main thread.
When the UI is frozen, I pause using XCODE and the last two lines in stacktrace:
0x103f0ea30 <+809>: callq 0x103f1554d ; _dispatch_thread_semaphore_wait
dispatch_sync(someQueue, block); // this is my code.
How can I find what is causing the block?
Any other suggestion to find out what is causing the block?
It always blocks on the same line/code.
In the Debug navigator (cmd-6), you should have a list of threads. One OTHER thread in there should be waiting for someQueue as well. I can't think off hand of a case where that wasn't the case. Usually the two threads are waiting for each other, (e.g. via dispatch_sync).
For example, you might have this:
dispatch_sync(mySerialQueue, ^{
[self foo];
});
and
- (void)foo
{
dispatch_sync(mySerialQueue, ^{
...
});
}
The latter will be waiting for the former to finish forever, because the former is holding onto myQueue until it finishes the call to -foo.
(Note that mySerialQueue has to be created with the DISPATCH_QUEUE_SERIAL for this to happen.)
Trying my hand with blocks in Objective C.
I ran into a strange problem.
Below i have created a block and submitted the block for asynchronous execution on a global dispatch queue.
It doesn't print anything for me. When i replaced the keyword async with sync it works fine and prints the result immediately.
#implementation BlockTest
+(void) blocksTest{
__block int m = 0;
void (^__block myblock)(void);
myblock = ^(){
NSLog(#"myblock %u ", ++m);
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"dispatch_async dispatch_get_global_queue");
myblock();
});
}
#end
int main(int argc, const char * argv[])
{
[BlockTest blocksTest];
}
Can someone please help me with this problem ?
Your program is exiting before the block has a chance to run.
The nature of an asynchronous call like dispatch_async() is that it returns and allows the caller to continue on, possibly before the task that was submitted has completed. Probably before it has even been started.
So, +blocksTest returns to the call site in main(). main() continues to its end (by the way, without returning a value, which is bad for a non-void function). When main() returns, the whole process exits. Any queues, tasks, worker threads, etc. that GCD was managing is all torn down during process termination.
The process does not wait for all threads to exit or become idle.
You could solve this by calling dispatch_main() after the call to +blocksTest in main(). In that case, though, the program will never terminate unless you submit a task which calls exit() at some point. For example, you could put the call to exit inside the block you create in +blocksTest.
Actually, in this case, because the task would run on a background thread and not the main thread, anything which delays the immediate exit would be sufficient. For example, a call to sleep() for a second would do. You could also run the main run loop for a period of time. There's no period of time that's guaranteed to be enough that the global queue has had a chance to run your task to completion, but in practical terms, it would just need a fraction of a second.
There's a complication in that methods to run the run loop exit if there are no input sources or timers scheduled. So, you'd have to schedule a bogus source (like an NSPort). As you can tell, this is a kludgy approach if you're not otherwise using the run loop for real stuff.
Because when you used dispatch_async, the block may have started the execution, but haven't reached the point where you print. However, if you use dispatch_sync, it makes sure that the entire block execution is done before it returns. Remember, dispatch_sync is the main thread.
I've been using NSOperationQueue's addOperationWithBlock: to run code in background threads, like so:
self.fetchDataQueue = NSOperationQueue()
for panel in self.panels {
self.fetchDataQueue.addOperationWithBlock() {
() -> Void in
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
//Background code
}
}
}
I'm concerned that I may be doing this wrong. I can't see a way that the fetch queue would be able to know when an operation is done, since there's no completion to call, and I'm not confident it's tracking activity across threads to make sure it's still going.
And the point of using this is so that I don't queue them up in single file and take much longer to process, and so I don't run them all at once and use too much memory.
EDIT: I'm aware that I don't need to be doing dispatch_async, but it's simply an example of some block-based code I may call which may do the same thing, or a web request which may get back after a delay.
Well, your code will run in a background block. If you are using a queue to make sure that one operation only starts when the next one is finished, you may be in trouble: The block that you happen to the NSOperationQueue has finished as soon as it has dispatched the background code to GCD, not when the background code has actually finished which may be much later.