I'm performing a somewhat CPU intensive operation (TensorFlow model inference)on a background thread. However, it isn't until I interrupt the main thread that it appears the CPU utilization is realized. For example:
//process model on background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
ModelData* data = [_processor processData:image];
//now process the results on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self processData:data];
});
});
[self processData:data] isn't doing anything expensive at all and nothing I would expect to spike the processor. More interesting is if I remove the main thread processing all together (and only interrupt the main thread to do nothing) the CPU still sees excessive CPU utilization. For example:
//process model on background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
ModelData* data = [_processor processData:image];
//now process the results on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
//do nothing, only interrupt the main thread
});
});
If I remove the call to dispatch to the main thread, I see no excessive CPU utilization at all. For example:
//process model on background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
ModelData* data = [_processor processData:image];
)};
So it appears that just dispatching to the main thread rails the CPU.
Any idea why this is happening? Thanks!!
Related
I'm trying to understand queue in iOS; with this code
dispatch_queue_t coda_thread=dispatch_queue_create("coda_thread",NULL);
//UIPROGRESS VIEW
for(i=0;i<=10;i=i+1)
{
dispatch_async(coda_thread,
^{
NSLog(#"CODA_THREAD");
NSLog(#"attendo..");
[NSThread sleepForTimeInterval:10];
dispatch_async(dispatch_get_main_queue(),
^{
NSLog(#"MAIN THREAD");
NSLog(#"aggiorno barra..");
[self.upv setProgress:i/10 animated:YES];
});
});
}
I expected no freeze in GUI because sleep is in coda_thread (and not in main queue where is updated the GUI) queue while setProgress in main queue.. Instead I have freeze in my GUI..why this?
The problem is that a dispatch queue is not a new thread. You have no guarantee that the dispatch queue is actually using a different thread. Combining GCD API with thread API just won't work.
please consider following code:
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, myQueue, ^{
NSLog(#"First block of code");
});
dispatch_group_async(myGroup, myQueue, ^{
NSLog(#"Second block of code");
});
dispatch_group_async(myGroup, myQueue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{ //problem here
dispatch_time_t myTime = dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC);
dispatch_after(myTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Third block of code work");
});
});
});
dispatch_group_wait(myGroup, DISPATCH_TIME_FOREVER);
NSLog(#" All done ?");
Problem is in dispatch_sync(dispatch_get_main_queue(), ^{
When i make dispatch_async(dispatch_get_main_queue(), ^{, it works as it should. Point of making that sync, is blocking main thread for 10 seconds, simulating "hard work". But, unfortunately, its not work. I wonder why?
What i want is: dispatch_sync block main thread for only 10 seconds, then execution continues.
What in fact happening: dispatch sync block whole application from any further execution.
If that code is running on the main event loop, then the dispatch_group_wait() is going to block the main queue. That prevents the synchronous execution of any block on the main queue and leads to deadlock.
You can verify this by pausing into the debugger. You'll likely see the main thread/queue blocked on the call to wait and a secondary thread/queue blocked on dispatch_sync.
Your code is presumably run on the main queue. It dispatches three blocks on the background, then waits until all three blocks have finished running. Since this is done on the main queue, other blocks will only start running on the main queue afterwards, that is after all the three async blocks that you queued up have finished.
Now the third of these three blocks calls the main thread with a sync call. What that means is it adds a block to the main queue, then it waits until the block starts executing, then it waits further until the block finishes executing, then the dispatch_sync call returns to its caller.
Well, that block dispatched to the main thread cannot start executing, because the main thread is busy waiting for the three async blocks to finish, and one of them won't finish because it is waiting for the block dispatched to the main thread to start executing, which it can't because and so on and so on forever. It doesn't actually matter one bit what you are trying to do in the synchronous block, because that block never gets as far as starting to execute.
I am aware that calling dispatch_async in the current queue will cause a deadlock, however, experiencing a deadlock in a completely different queue:
-(void) deadlock{
// we reach this point in the main queue
dispatch_sync(dispatch_queue_create("lucas", 0), ^{
NSLog(#"Doing something in the bakcgound...");
// We reach this point in another queue, but it deadlocks!
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Will I ever get here?????");
});
});
}
Any idea of what I'm doing wrong??
Yes, it's a deadlock. Just like you have been creating an example of one
-(void) deadlock{
// we reach this point in the main queue
//and so main queue waits till this task will finish
dispatch_sync(dispatch_queue_create("lucas", 0), ^{
//doing something in background
//while main queue still blocked by that dispatch_sync() call
NSLog(#"Doing something in the bakcgound...");
// We reach this point in another queue, but it deadlocks!
//Of cause it does!!! Main queue is still waiting till task dispatched to "lucas" queue synchronously finishes. But it can't - waits for main queue to perform this later bock synchronously
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Will I ever get here?????");
});
});
}
//we reach this point only after all code written above will performed. so, in our case, this point is unreachable either way
So, dependently on your task, you should change one of this dispatch_sync() to dispatch_async().
Dispatch_sync is semantically equivalent to a traditional mutex lock, rather than creating a thread. Try the following, will give you: "Is main thread: 1".
dispatch_sync(dispatch_queue_create("lucas", 0), ^{
NSLog(#"Doing something in the bakcgound...");
NSLog(#"Is main thread: %d", [NSThread isMainThread]);
// We reach this point in another queue, but it deadlocks!
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Will I ever get here?????");
});
});
What you want I think is more or less the following:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
NSLog(#"Doing something in the bakcgound...");
dispatch_async(dispatch_get_main_queue(), ^(void){
NSLog(#"Will I ever get here?????");
});
});
Is there any difference between if dispatch_sync is called in 3 different queue like
1.
dispatch_sync(dispatch_get_main_queue(),^(void){
NSLog(#"this execute in main thread") // via [NSThread isMainThread]
});
2.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){
NSLog(#"this also execute in main thread") // via [NSThread isMainThread]
}
3.
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_sync(queue, ^(void){
NSLog(#"this also execute in main thread") // via [NSThread isMainThread]
}
Whenever i call dispatch_sync, block executed in main thread, without considering in which queue it is dispatched. So why this function take queue as a argument as it doesn't use it. Can someone please clarify this?
dispatch_sync is a blocking operation. That is, the function will not return until the work represented in the block is completed.
When dispatched to an asynchronous queue -- like one of the global queues or a concurrent queue of your own making -- there is no reason to do anything but invoke the block on the thread that called dispatch_sync(). Even in the case of invoking the block on a synchronous queue, the dispatch_sync() is going to wait until completion anyway so, internally, it might as well stop until the rest of the work is done in the queue and then execute the block directly.
As it turns out, passing data from thread A to thread B is expensive. If the queue is in the state where execution can happen immediately, then dispatch_sync will fast path the execution by simply calling the block on the thread that dispatch_sync was called on.
And, by definition, you shouldn't care. The calling thread is blocked -- can't do a thing -- until dispatch_sync() returns.
So, really, all of this is an implementation detail. GCD is free to execute the blocks on whatever threads it deems most appropriate. It just so happens that don't context switch is often the most important rule of figuring that out.
See dispatch_sync documentation, which notes
As an optimization, this function invokes the block on the current thread when possible.
If you dispatch something synchronously, since the thread must wait for the dispatched code to complete, anyway, it will frequently run that code on the current thread. So if dispatched synchronously from the main thread, it will run on main thread. If dispatched synchronously from a background thread, it will run on that background thread.
As noted by ipmcc, a well-known exception is when a background thread dispatches something synchronously to the main thread. As the libdispatch source says:
It's preferred to execute synchronous blocks on the current thread
due to thread-local side effects, garbage collection, etc. However,
blocks submitted to the main thread MUST be run on the main thread.
For your problem: you call the dispatch_sync always in main queue, and if you wanna know why, see the following:
At first you need to pay attention to the description on of "dispatch_sync"
Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async,
this function does not return until the block has finished.
Calling this function and targeting the current QUEUE(NOT THREAD) results in deadlock.
#define logStep(step,queue) NSLog(#"step: %d at thread: %# in -- queue: %s",step,[NSThread currentThread],dispatch_queue_get_label(queue));
// call the method in main thread within viewDidLoad or viewWillAppear ...
- (void)testDispatchSync{
//let's distinctly tell the 4 queues we often use at first
self.concurrentQ = dispatch_queue_create("com.shared.concurrent", DISPATCH_QUEUE_CONCURRENT);
self.serialQ = dispatch_queue_create("com.shared.serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQ = dispatch_get_main_queue();
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
logStep(1,mainQ) //we're in main thread and main queue, current queue IS main queue
// do a sync in main thread & concurrent queue
dispatch_sync(_concurrentQ, ^{
logStep(2,_concurrentQ)
});
// do a sync in main thread & serial queue
dispatch_sync(_serialQ, ^{
logStep(3,_serialQ)
});
//uncommenting the following code that you'wll see a crash will occur, because current queue is main queue
// dispatch_sync(mainQ, ^{
// logStep(4, mainQ)
// });
dispatch_async(_concurrentQ, ^{
// inside of the this scope, current queue is "_concurrentQ"
logStep(11,_concurrentQ)
// using sync in any queue here will be safe!
dispatch_sync(_concurrentQ, ^{
logStep(12,_concurrentQ)
});
dispatch_sync(_serialQ, ^{
logStep(13,_concurrentQ)
});
dispatch_sync(mainQ, ^{
logStep(14,mainQ)
});
dispatch_sync(globalQ, ^{
logStep(15,globalQ)
});
// using async in any queue here will be safe!
dispatch_async(_concurrentQ, ^{
logStep(111,_concurrentQ)
});
dispatch_async(_serialQ, ^{
logStep(112,_concurrentQ)
});
dispatch_async(mainQ, ^{
logStep(113,mainQ)
});
dispatch_async(globalQ, ^{
logStep(114,globalQ)
});
});
dispatch_async(_serialQ, ^{
// inside of the this scope, current queue is "_serialQ"
logStep(21,_serialQ)
// using async in any queue except current queue here will be safe!
dispatch_sync(_concurrentQ, ^{
logStep(22,_concurrentQ)
});
dispatch_sync(mainQ, ^{
logStep(23,mainQ)
});
dispatch_sync(globalQ, ^{
logStep(24,globalQ)
});
//uncommenting the following code that you'wll see a crash will occur, because current queue is "_serialQ"
// dispatch_sync(_serialQ, ^{ //app will die at here
// logStep(25,_serialQ)
// });
});
}
So we got the conclusion:
the key problem is that thread will be blocked while "dispatch_sync" running on current queue which is a serial queue at the same time.
main queue is also a serial queue so that it explained why you cann't call dispatch_sync in main thread
I am new with MKNetworkKit and I have a little design issue. I am trying to process the data fetched by the MKNetworkOperation on a background thread but I am not sure where to do that in order to keep the design clean.
[op onCompletion:^(MKNetworkOperation *completedOperation) {
NSDictionary *jsonDictionary = [completedOperation responseJSON];
// This part is always called on the main thread but
// I want to process here my jsonDictionary on a background thread
// to avoid blocking the main thread
} onError:^(NSError* error) {
errorBlock(error);
}];
The process time of my jsonDictionary takes a long time and I really don't want to do that on the main thread, how would you recommend me to switch to the background thread while keeping the design and philosophy of MKNetworkKit clean?
Thanks,
Martin
Within your completion handler use this code.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//asynchronous code
dispatch_async(dispatch_get_main_queue(), ^{
//synchronous code
});
});