Why dispatch_sync( ) call on main queue is blocking the main queue? - iphone

I know this is not a strong question but I have to clear my mind on this concept.
I have defined myBlock as follows.
void(^myBlock)(void) = ^{
for(int i = 0;i < 10 ; i++)
{
NSLog(#"%d and current queue = %#",i,[NSThread currentThread]);
}
};
Now In viewDidLoad method when I uses the dispatch_sync() method independently on main queue then the main queue gets blocked.
Here is the Sample.
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue,myBlock);
}
But But, when I use the same dispatch_sync() function on main thread Inside a block of dispatch_async() function which is fired on concurrent queue then the main thread does not blocked.
Here is the sample.
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue,^{
dispatch_sync(dispatch_get_main_queue(),myBlock);
});
}
I am not clear why this is happening? Why main thread blocked when calling dispatch_sync() independently?

When using dispatch_sync on a serial queue (like the main queue) the current thread has to wait until the dispatched code is executed.
A dead lock occurs when a block is dispatched synchronously on from a serial queue to the same queue.

There is only one main queue. In your first example, viewDidLoad is running on it. You then tell viewDidLoad to wait (i.e. "sync") on something else that's going to run on the main queue. They both can't be on it at exactly the same time.
In your second example, it's the concurrent queue that's being told to wait. That's not a problem because by doing dispatch_async, viewWillLoad is giving up the main queue and making it available for your block to run.

Dispatching a block on main queue is equivalent of calling it on the main thread. Main queue executes on main thread.
Since you are dispatching using dispatch_sync this will be a blocking call as dispatch_sync,
Submits a block object for execution on a dispatch queue and waits until that block completes.

Just need to understand this:
dispatch_sync() blocks the dispatch queue, submit the block to it and
waits until the submitted block completes.
dispatch_async() submits the block for asynchronous execution on dispatch queue and returns immediately.

When you run an async task, it will create a new thread and your code in the block will be executed in that new thread. In that method you call dispatch_sync on main thread because you want to run in the main queue. Please try to understand it with this example.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
if ([NSThread isMainThread])
{
NSLog(#"Running on main Thread in dispatch_async");
}
else
{
NSLog(#"Running on another Thread in dispatch_async");
}
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
if ([NSThread isMainThread])
{
NSLog(#"Running on main Thread in dispatch_sync");
}
else
{
NSLog(#"Running on another Thread in dispatch_sync");
}
});
dispatch_sync(dispatch_get_main_queue(), ^(void) {
if ([NSThread isMainThread])
{
NSLog(#"Running on main Thread in dispatch_sync");
}
else
{
NSLog(#"Running on another Thread in dispatch_sync");
}
});
});
Output is:
Running on another Thread in dispatch_async
Running on another Thread in dispatch_sync
Running on main Thread in dispatch_sync

Related

Perfoming UI updates in secondary queue

I am using the new CoreMotion framework to monitor some of the hardware devices. Here is the typical code to do that:
-(void)startAccelerometer{
self.motion.accelerometerUpdateInterval = 1/30.0f;
NSOperationQueue* accelerometerQueue = [[NSOperationQueue alloc] init];
CMAccelerometerHandler accelerometerHandler = ^(CMAccelerometerData *accelerometerData, NSError *error) {
NSLog(#"Accelerometer realtime values");
NSLog(#"x=%f", accelerometerData.acceleration.x);
NSLog(#"y=%f", accelerometerData.acceleration.y);
NSLog(#"z=%f", accelerometerData.acceleration.z);
NSLog(#" ");
};
[self.motion startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:[[accelerometerHandler copy]autorelease]];
}
That works just fine. Now I want to print the values on a UILabel, but since the CoreMotion frameworks has you use blocks, this is not guaranteed to be in the main queue (and in fact isn't for my app). Is it is "wrong" to just run the label's setter on the main queue like this?
-(void)startAccelerometer{
self.motion.accelerometerUpdateInterval = 1/30.0f;
NSOperationQueue* accelerometerQueue = [[NSOperationQueue alloc] init];
CMAccelerometerHandler accelerometerHandler = ^(CMAccelerometerData *accelerometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.lblAccelerometer.text = [NSString stringWithFormat:#"Accelerometer:\nx = %f\ny = %f\nz = %f",
accelerometerData.acceleration.x,
accelerometerData.acceleration.y,
accelerometerData.acceleration.z];
});
};
[self.motion startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:[[accelerometerHandler copy]autorelease]];
}
It works just fine and I don't really see any reason why this would be frowned upon. Any thoughts on that?
This is a common method that I use in many projects. UI updates must occur on the main thread.
//Dispatch on background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//background processing goes here
//Dispatch on main thread
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
});
});
In your case, your UI updates are occurring on the main thread. So I wouldn't worry about changing anything.
You are missunderstanding the concept of blocks, to put it simple:
Blocks are small pieces of code that can be handled as variables and be executed at a certain time or thread.
All UI updates MUST be performed on the main thread so as long as you do this it will be fine.
Codes can be executed in different threads with different priorities in sync or async mode. On your code you are doing it perfectly fine, you not only dispatch it to the Main Queue which is where uiupdates should be executed, but you are also dispatching it async which is the safest way to update send to the main queue (from your code i cannot tell if you are running this specific piece of code from the main queue or a secondary queue but if u were to dispatch a sync block from the main queue to the main queue your program would stop working)
For iOS documentation:
Use the dispatch_get_main_queue function to get the serial dispatch
queue associated with your application’s main thread. This queue is
created automatically for Cocoa applications and for applications that
either call the dispatch_main function or configure a run loop (using
either the CFRunLoopRef type or an NSRunLoop object) on the main
thread.
Read this here http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

Updating UI components from an async callback (dispatch_queue)

how can i update GUI elements with values from a queue?
if i use async queue construct, textlable don't get updated.
Here is a code example i use:
- (IBAction)dbSizeButton:(id)sender {
dispatch_queue_t getDbSize = dispatch_queue_create("getDbSize", NULL);
dispatch_async(getDbSize, ^(void)
{
[_dbsizeLable setText:[dbmanager getDbSize]];
});
dispatch_release(getDbSize);
}
Thank you.
As #MarkGranoff said, all UI needs to be handled on the main thread. You could do it with performSelectorOnMainThread, but with GCD it would be something like this:
- (IBAction)dbSizeButton:(id)sender {
dispatch_queue_t getDbSize = dispatch_queue_create("getDbSize", NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(getDbSize, ^(void)
{
dispatch_async(main, ^{
[_dbsizeLable setText:[dbmanager getDbSize]];
});
});
// release
}
Any UI update must be performed on the main thread. So your code would need to modified to use the main dispatch queue, not a queue of your own creation. Or, any of the performSelectorOnMainThread methods would work as well. (But GCD is the way to go, these days!)

Make sure function runs on main thread only

How can I make sure that my function is run only on the main thread? It updates UI elements.
Is a function like this considered 'bad'?
-(void)updateSomethingOnMainThread {
if ( ![[NSThread currentThread] isEqual:[NSThread mainThread]] )
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
else {
// Do stuff on main thread
}
}
I wrote it like this to avoid having a second function, initially I had it like this:
-(void)updateSomethingOnMainThread_real {
// Do stuff on main thread
}
-(void)updateSomethingOnMainThread {
[self performSelectorOnMainThread:#selector(updateSomethingOnMainThread_real) withObject:nil waitUntilDone:NO];
}
As an alternative to ayoy's method-based GCD implementation for guaranteeing execution on the main thread, I use the following GCD-based function in my code (drawn from another answer of mine):
void runOnMainThreadWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
You can then use this helper function anywhere in your code:
runOnMainThreadWithoutDeadlocking(^{
// Do stuff that needs to be on the main thread
});
This guarantees that the actions taken in the enclosed block will always run on the main thread, no matter which thread calls this. It adds little code and is fairly explicit as to which code needs to be run on the main thread.
This is fine. You can also use GCD to execute code on the main thread.
Checkout this SO post.
GCD to perform task in main thread
I wrote this simple #define which I've been using with great success:
#define ensureInMainThread(); if (!NSThread.isMainThread) { [self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO]; return; }
That way your method, assuming it's parameterless, looks like this
- (void) updateTheThings {
ensureInMainThread();
[self.dog setTailWag:YES];
// etc...
Alternatively, you can use Grand Central Dispatch API, but it's not very handy:
-(void)updateSomethingOnMainThread {
void (^doStuff)(void) = ^{
// stuff to be done
};
// this check avoids possible deadlock resulting from
// calling dispatch_sync() on the same queue as current one
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (mainQueue == dispatch_get_current_queue()) {
// execute code in place
doStuff();
} else {
// dispatch doStuff() to main queue
dispatch_sync(mainQueue, doStuff);
}
}
otherwise, if synchronous call isn't needed, you can call dispatch_async() which is much simpler:
-(void)updateSomethingOnMainThread {
dispatch_async(dispatch_get_main_queue(), ^{
// do stuff
});
}

suspending dispatch timers from main_queue when it was created on another one

Can you suspend a GCD timer from a queue besides the one it's schedule to run on?
I have a timer, created on the global_queue with low priority and when it fires, I manipulate some UI work via the main_queue. For some states in the UI, I have to suspend the timer. Do I have to switch from the main_queue back to the low priority queue to perform the suspend?
dispatch_queue_t lowPriQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
myTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, lowPriQ);
dispatch_source_set_timer(myTimer,
startTime, // now
interval, // 15 seconds
2000ull);
// configure the event handler
dispatch_source_set_event_handler(myTimer, ^{
NSLog(#"Timer fired");
// UI Work
dispatch_async(dispatch_get_main_queue(), ^ {
[self doSomeButtonEnableDisable]
});
});
dispatch_resume(myTimer); // start the timer
- (void)doSomeButtonEnableDisable
{
if (someParticularState) {
// Turn off the timer
// Should I suspend the timer on the low priority global queue
// or is it valid to suspend on the main queue?
dispatch_queue_t lowPriQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(lowPriQ(), ^ {
dispatch_suspend(myTimer);
});
}
}
Yes, it's valid to suspend a dispatch object from any queue. If a block is currently running when dispatch_suspend() is called, that block will complete execution and subsequent scheduled blocks will be prevented from executing.

How to send selectors or blocks to an NSRunLoop to perform?

I need to guarantee that the same thread performs various actions at arbitrary times. First the thread needs to initialize a library, then I want the thread to sleep until work needs to be done and upon user input, I need to be able to pass selectors or blocks for execution.
How can I setup an NSRunLoop to sleep after initialization? After which, how do I signal the run loop to wake up and do something?
I've tried reading the Threading Programming Guide for iOS, but I'd like to avoid setting up classes as custom input classes and use something more lightweight like performSelector:onThread:
Can I set a timer to fire forever from now so the run loop doesn't end?
Here's essentially what I want in pseudo-code:
// Initialization Code...
do {
sleepUntilSignaled();
doWorkSentToThisThread();
while (!done);
Where I send the work to do as a performSelector:onThread: message. It would be even better if I could send the run loop a block like: ^{[someObj message]; [otherObj otherMsg];} but I'd be happy with performSelector since I'm pretty sure that's possible without much extra coding.
Thanks!
You have all the necessary pieces together in your question. You start your thread and have it run it’s runloop. If you need the thread to do something you can use performSelector:onThread: on the main thread to do it.
There is one thing with the runloop you have to be aware though: It won’t run unless it has an input source or a timer attached to it. Just attach a timer to the run loop that fires some time in the distant future and you’re all set.
// Initialization code here
[NSTimer scheduledTimerWithTimeInterval: FLT_MAX
target: self selector: #selector(doNothing:)
userInfo: nil repeats:YES];
NSRunLoop *rl = [NSRunLoop currentRunLoop];
do {
[rl runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);
Using performSelector:onThread:withObject: you can also pass your block to the background thread. All you need to do is to write a method somewhere that takes an block as a parameter and runs it:
#interface NSThread (sendBlockToBackground)
- (void) performBlock: (void (^)())block;
#end
#implementation NSThread (sendBlockToBackground)
- (void) performBlock: (void (^)())block;
{
[self performSelector: #selector(runBlock:)
onThread: self withObject: block waitUntilDone: NO];
}
- (void) runBlock: (void (^)())block;
{
block();
}
#end
But maybe you should use a dispatch queue instead of all this. This requires less code and probably has less overhead also:
dispatch_queue_t myQueue = dispatch_queue_create( "net.example.product.queue", NULL );
dispatch_async( myQueue, ^{
// Initialization code here
} );
// Submit block:
dispatch_async( myQueue, ^{
[someObject someMethod: someParameter];
} );
A dispatch queue created using dispatch_queue_create is a serial queue - all blocks sent to it will be performed in the same order they arrived, one after another.
Consider using NSConditionLock. It is designed for tasks like this. Imagine that you have a queue with data. First thread adds data to queue, second thread waits for data and processes it.
id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];
//First thread
while(true)
{
[condLock lock];
/* Add data to the queue. */
[condLock unlockWithCondition:HAS_DATA];
}
//Second thread
while (true)
{
[condLock lockWhenCondition:HAS_DATA];
/* Remove data from the queue. */
[condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)];
// Process the data locally.
}
I think you can use NSInvocationOperation with NSOperationQueue.