I have this timer created using GDC. It will call a method every 1 second. Is it safe to have this timer alive during the whole time, even in background?
self.theTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.theTimer, DISPATCH_TIME_NOW, (1.0) * NSEC_PER_SEC, 0.25 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.theTimer, ^{
[self awakeAndProcess];
});
// Start the timer
dispatch_resume(self.theTimer);
The method "awakeAndProcess" has a "consumer" behavior where it checks a data queue and tries to send an HTTP request. So it is constantly checking if there are messages to be sent
You are better off pausing the timer when going to background to conserve battery because the awakeAndProcess seems to be a network call. But if you are in background then all your tasks are suspended anyways so shouldn't be a problem. When in foreground its better to wait for the previous awakeAndProcess call to finish before you trigger the next call. Otherwise you might end up with lot of awakeAndProcess calls being batched together. If awakeAndProcess is not reentrant then it can cause havoc in your code.
You are better off suspending the timer after the awakeAndProcess and then call resume after the awakeAndProcess is fully complete.
If you do this then its safe to use your approach.
Related
I have function ,
-(void)serverFetch{
//server fetch
}
In every 15mintutes, i'm calling this method using NSTimer,
[NSTimer scheduledTimerWithTimeInterval:900.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
[self fetchFromServer];
}];
I'm using APNS in my app, so when we receive the notification , again i'm calling this method.
So Scheduler thread and this notification thread should not happen in parallel. For instance, when scheduler thread is in operation and push notification arrives then push notification thread should wait for scheduler thread.How can i achieve this?Any help appreciated?
One approach would be to use Grand Central Dispatch (GCD). Create a serial queue and add blocks to it for asynchronous execution when your timer fires or notifications arrive, the blocks will be executed strictly one after the other. This will only work correct if the work each block does is completely synchronous, that is when the block returns all its work is complete.
If your blocks contain asynchronous work then you will also need a semaphore. A block should acquire the semaphore when it starts execution and its final asynchronous action should release it. In this way though the block scheduled by the serial queue returns and the queue starts the next block that next block will immediately block waiting to acquire the semaphore until the previous block's last asynchronous action releases it.
If after studying GCD, designing a solution, and implementing it you have a problem ask a new question, show the code you have written, and explain the problem. Someone will undoubtedly help you move forward.
HTH
Today i've tried following code:
- (void)suspendTest {
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0);
dispatch_queue_t suspendableQueue = dispatch_queue_create("test", attr);
for (int i = 0; i <= 10000; i++) {
dispatch_async(suspendableQueue, ^{
NSLog(#"%d", i);
});
if (i == 5000) {
dispatch_suspend(suspendableQueue);
}
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"Show must go on!");
dispatch_resume(suspendableQueue);
});
}
The code starts 10001 tasks, but it should suspend the queue from running new tasks halfway for resuming in 6 seconds. And this code works as expected - 5000 tasks executes, then queue stops, and after 6 seconds it resumes.
But if i use a serial queue instead of concurrent queue, the behaviour is not clear for me.
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0);
In this case a random number of tasks manage to execute before suspending, but often this number is close to zero (suspending happens before any tasks).
The question is - Why does suspending work differently for serial and concurrent queue and how to suspend serial queue properly?
As per its name, the serial queue performs the tasks in series, i.e., only starting on the next one after the previous one has been completed. The priority class is background, so it may not even have started on the first task by the time the current queue reaches the 5000th task and suspends the queue.
From the documentation of dispatch_suspend:
The suspension occurs after completion of any blocks running at the time of the call.
i.e., nowhere does it promise that asynchronously dispatched tasks on the queue would finish, only that any currently running task (block) will not be suspended part-way through. On a serial queue at most one task can be "currently running", whereas on a concurrent queue there is no specified upper limit. edit: And according to your test with a million tasks, it seems the concurrent queue maintains the conceptual abstraction that it is "completely concurrent", and thus considers all of them "currently running" even if they actually aren't.
To suspend it after the 5000th task, you could trigger this from the 5000th task itself. (Then you probably also want to start the resume-timer from the time it is suspended, otherwise it is theoretically possible it will never resume if the resume happened before it was suspended.)
I think the problem is that you are confusing suspend with barrier. suspend stops the queue dead now. barrier stops when everything in the queue before the barrier has executed. So if you put a barrier after the 5000th task, 5000 tasks will execute before we pause at the barrier on the serial queue.
As we all know, under NSDefaultRunLoopMode, NSTimer will not fire when the scrollView is scrolling. If we need the timer works, we should add it to NSRunLoopCommonModes.
Consider that,
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, aQueue);
dispatch_resume(timer); // Is the place for `dispatch_resume(timer);` appropriate? Or we should
always put it after `dispatch_source_set_event_handler`?
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, ti * NSEC_PER_SEC, ti * 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
if (anAction) anAction();
});
when I create timer based on GCD like above, will the timer be hang or ignored, in other words could not fire, under some condition? Or it will run forever?
Here's my thinking:
What if the queue is suspended?
If it doesn't run on the queue, the timer will continue running regardless of the queue's status.
If it does run on the queue, the timer is suspended as well as the queue.
Where does the timer run?
According to the doc, dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, aQueue); means :
Creates a new dispatch source to monitor low-level system objects and
automatically submit a handler block to a dispatch queue in response
to events.
it says that we submit the event handler to the argument dispatch_queue_t queue, that is we will respond to the event on the queue. But it doesn't mean the timer runs on the queue.
Thanks :)
I have 2 classes, ViewController class, and Worker. All the code that I need to run in the background is in the Worker class.
My ViewController looks something like this:
- (void)viewDidLoad {
//create an instance of 'Worker'
}
- buttonClick {
//call the 'manager' method in the worker instance that was just created (do this method as a background thread)
}
My Worker class looks something like this:
- (void)manager {
//call 'repeat' method as a background thread
}
- (void)repeat {
//call 'innerWorker' method as a background thread
}
- (void)innerWorker {
//do work
}
The repeat method needs to get run every second.
I've tried the 2 following ways to make the repeat method run every second.
Method 1 :
timerObj = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(repeatMethod) userInfo:nil repeats:YES];
Method 2:
Putting this code at the end of repeat:
dispatch_queue_t q_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, q_background, ^(void){
[self repeatMethod];
});
Both of these 2 methods work fine as long as the app is in the foreground but the moment I press the home button, the repeat method runs one last time, but does not call the innerWorker method and then the app is suspended. I know this by using NSLogs all over the place.
I realize Method 2 is a bit of a hack but that's fine as this is an internal app that I wont be publishing.
All the methods are called as background threads using this code: eg:
dispatch_queue_t q_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(q_background, ^{
[self repeatMethod];
});
I'm new to iOS so maybe I'm missing something small here. I just want my app to keep running in the background. Please help me.
First of all: the dispatch_queue priority has nothing to do with running in background ... it is the priority in the queue.
Here are the info:
DISPATCH_QUEUE_PRIORITY_HIGH Items dispatched to the queue will run at high priority, i.e. the queue will be scheduled for execution before any default priority or low priority queue.
DISPATCH_QUEUE_PRIORITY_DEFAULT Items dispatched to the queue will run at the default priority, i.e. the queue will be scheduled for execution after all high priority queues have been scheduled, but before any low priority queues have been scheduled.
DISPATCH_QUEUE_PRIORITY_LOW Items dispatched to the queue will run at low priority, i.e. the queue will be scheduled for execution after all default priority and high priority queues have been scheduled.
DISPATCH_QUEUE_PRIORITY_BACKGROUND Items dispatched to the queue will run at background priority, i.e. the queue will be scheduled for execution after all higher priority queues have been scheduled and the system will run items on this queue on a thread with background status as per setpriority(2) (i.e. disk I/O is throttled and the thread's scheduling priority is set to lowest value).
For "real" background operations check this:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
The answer to your question depends on what you want to do.
Only the following types of apps, with the appropriate plist flag set, can execute a background process while the app is still in the foreground:
Apps that play audible content to the user while in the background,
such as a music player app
Apps that record audio content while in the background
Apps that keep users informed of their location at all times, such
as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
If your app falls outside of one of those categories, and will go through app store approval, you will need to devise another strategy for doing the background work you desire to preform.
More info can be found here:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
I have a thread that listens on a single UDP socket, but also needs to wake up once in a while to perform other tasks. These tasks are triggered by the passage of time, or by activity on other threads. My current design is to use select() timeout value as scheduling timer, and to write a packet to the socket (loopback) address when I need to wake it from another thread.
However, Apple documention says select() timeouts should not be used to wake up more than a few times per second. And, in practice, I find they may be delayed by 100 msec or more, whereas I would like 10-20 msec resolution. Are they just trying to discourage cpu intensive polling, or is there something wrong with using select() per se. Is there a better approach?
Would it help to replace select with kqueue/kevent? Or, create a dedicated scheduling thread, with mach_wait_until() to handle the timer, and then write to the socket to wake the net thread? Or, do all the work in the dedicated thread, and have the net thread queue incoming data to it?
Something bugs me about this approach. Why do you have anything at all happening on the select() thread?
If you need a thread dedicated to waiting for incoming packets, them make that thread a wait as much as possible.
while (1) {
int numsockets = select(…);
if (numsockets > 0) {
// Read data (only drain the socket)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Process data
});
}
}
Then you can have your periodic tasks run using timer dispatch sources.
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_event_handler(source, ^{
// Periodic process data
});
uint64_t nsec = 0.001 * NSEC_PER_SEC;
dispatch_source_set_timer(source, dispatch_time(DISPATCH_TIME_NOW, nsec), nsec, 0);
dispatch_resume(source);
I don't know, but I've been told that you can even use a dispatch source to replace the select().