Im executing a thread that keeps looking for updates from a web site. It should be possible to set the refresh rate in some view.
The update thread keeps checking for a updated interval. But I would like to avoid race conditions. (Do I even have to worry for this with GCD?)
//This variable is used to avoid race conditions, refreshRate is a instance variable
int threadRefreshRate = refreshRate;
BOOL autoRefresh = YES;
dispatch_async(autoUpdateQueue, ^ {
while(YES){
NSLog(#"Runs autoupdate thread");
dispatch_async(dispatch_get_main_queue(), ^{
if(autoRefresh){
[self checkForUpdate];
//Trying to set thread variable to avoid race condition
threadRefreshRate = refreshRate;
}
else
NSLog(#"Should not auto refresh");
});
sleep(threadRefreshRate);
}
});
I tried to implement this code. However it doesn't work to assing the 'thread'-variable within a block.
For the kind of code you have given, i would use the timer events raised on the queue instead doing explicit sleep in the code. This way you dont have to worry about the race conditions etc..
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (!timer) return;
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), refreshRate * NSEC_PER_SEC, 5 * NSEC_PER_SEC);
dispatch_source_t timer = self.timer;
//initialize self to blockSelf with __block
self.timerAction = ^{
[blockSelf checkForUpdate];
};
dispatch_source_set_event_handler(timer, timerAction);
dispatch_resume(timer);
When the autoRefresh is set to NO, you can cancel it by
dispatch_source_cancel(timer);
Related
I used before one timer in my app for periodically launched task (token refresh actually). I found code example on stackoverflow and it worked for me.
This is the code example definition (above implementation header):
dispatch_source_t CreateDispatchTimer(double interval, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer)
{
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
this is the variables definitions:
dispatch_source_t _timer;
static double SECONDS_TO_FIRE = 60.000f;
and then there is a method e.g. startTimer where I launched this timer:
- (void)startTimer {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = CreateDispatchTimer(SECONDS_TO_FIRE, queue, ^{
// NSLog(#"timer is fired");
// do smth I need
}
});
}
So this code worked for me very well.
Now I need another (second) timer for separate task that should be fired in own time interval.
So I copied code above in separate class, set up another time interval.
Faced that if I use the same name for dispatch_source_t - like CreateDispatchTimer above, application won't be compiled!
So for second timer in separate class I changed the dispatch_source_t name to another, like CreateTimerDispatch. So app was compiled successfully.
But, the problem is - only second timer works now! The first time is not fired at all!
How to fix this? Can now understand why only last timer is fired.
Found the reason - except renaming dispatch_source_t CreateDispatchTimer block with different name for these 2 timers,
I needed to rename timer variable for second timer too.
I caught that the first timer was completely overwritten by second timer (as it was created later). Thus the first timer didn't fire at all.
So the working code example for second timer is following:
dispatch_source_t CreateLocationTimerDispatch(double interval, dispatch_queue_t queue, dispatch_block_t block) {
dispatch_source_t timerForLocationRefresh = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timerForLocationRefresh) {
dispatch_source_set_timer(timerForLocationRefresh, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10);
dispatch_source_set_event_handler(timerForLocationRefresh, block);
dispatch_resume(timerForLocationRefresh);
}
return timerForLocationRefresh;
}
variable:
dispatch_source_t _timerForLocationRefresh;
static double SECONDS_TO_FIRE = 1800.f; // time interval lengh in seconds 1800
calling:
- (void)startTimer {
// second timer
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timerForLocationRefresh = CreateLocationTimerDispatch(SECONDS_TO_FIRE, queue, ^{
// do smth
});
}
Summary:
CreateDispatchTimer block for second timer needed to be renamed, e.g. > CreateLocationTimerDispatch
timer variable for second timer needed to be renamed too, e.g. > timerForLocationRefresh
I have this situation: a video must be processed frame by frame but as frames are being processed, the output have to be write to file in sequence.
I want to fire asynchronous blocks using dispatch_async to a concurrent queue to speed the process but because this queue is asynchronous I don't see how I will coordinate to write the frames serially to the output.
Suppose this situation: frames 1, 2, 3, 4 and 5 are sent to concurrent queues for processing. Because any block can finish at any time, frame 4 may be the first to finish, followed by 5, 3, 1, 2. So how will I manage to write the frames in the sequential order to the output?
I have a code like this:
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
while (true) {
video >> frame; // read a frame from the video
dispatch_async(aQueue, ^{
processVideo(frame, outputFrame);
writeToVideo(outputFrame); // this is here just to show what needs to be done
});
// bla bla
}
any clues?
thanks
I would use a combination of a serial dispatch queue and an NSCondition. The serial queue makes sure that none of the writes happen concurrently, while the NSCondition makes sure that they happen in the correct order.
From the NSCondition docs:
A condition object acts as both a lock and a checkpoint in a given
thread. The lock protects your code while it tests the condition and
performs the task triggered by the condition. The checkpoint behavior
requires that the condition be true before the thread proceeds with
its task. While the condition is not true, the thread blocks.
In your specific situation I'd do something like this...
In your loop you first declare a BOOL (initially set to NO) which indicates if your frame has been processed or not, and an NSCondition. Then, dispatch_async to both the background queue to process frame, and the serial queue to write the data.
When the block in the serial queue runs, lock the NSCondition and then check the BOOL to see if the frame has been processed. If it has, proceed with the write. If it hasn't, wait for a signal from the NSCondition and check again when it receives it. When it's done, unlock the NSCondition.
When the block in the background queue runs, lock the NSCondition and process the frame. When the frame is processed, set the BOOL to indicate that the frame is processed. Then signal and unlock the NSCondition.
Note: It's important that you only access the BOOL that indicates that the frame is processed and your outputFrame inside the NSCondition's lock; the lock is making sure they stay synchronized between threads.
// Create the background and serial queues
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t writeQueue = dispatch_queue_create("writeQueue", DISPATCH_QUEUE_SERIAL);
while (true) { // I'm assuming you have some way to break out of this...
NSCondition *condition = [[NSCondition alloc] init];
// These need the __block attribute so they can be changed inside the blocks
__block BOOL frameProcessed = NO;
__block FrameType outputFrame = nil;
// video >> frame; // read a frame from the video
// dispatch the frame for processing
dispatch_async(backgroundQueue, ^{
[condition lock];
processVideo(frame, outputFrame);
frameProcessed = YES;
[condition signal];
[condition unlock];
});
// dispatch the write
dispatch_async(writeQueue, ^{
[condition lock];
while (!frameProcessed) {
[condition wait]; // this will block the current thread until it gets a signal
}
writeToVideo(outputFrame);
[condition unlock];
});
}
Note: There is a semi-subtle trick with BOOL frameProcessed in the above code too. Since it's declared inside the loop instead of outside, each block will capture the one associated with its frame.
Update: Adding an NSCondition for reading as well.
Because writing to video is slow compared to the parallel execution,
zillions of frames are allocated and sit in memory until they are
saved to disk.
I would handle this is by throttling the reads using another NSCondition which blocks your reads if there are too many frames waiting to be written in your writeQueue. The concept is almost identical to the NSCondition we added before, it's just a different condition; in this cast it would be an int that indicates how many frames are waiting to be written.
Before your loop, define a readCondition, writeQueueSize and maxWriteQueueSize. Inside the loop, first lock the readCondition, check if writeQueueSize >= maxWriteQueueSize. If it isn't, continue on with reading a frame and queuing up the processing and writing. Just before you dispatch to the writeQueue, increment writeQueueSize. Then unlock readCondition.
Then, inside the block dispatched to writeQueue, once the write is complete, lock readCondition, decrement writeQueueSize, and signal and unlock readCondition.
That should ensure that there are never more than maxWriteQueueSize blocks waiting in the writeQueue. If there are that many blocks waiting, it'll effectively pause the reading of frames from the video until the writeQueue is ready for more.
// Create the background and serial queues
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t writeQueue = dispatch_queue_create("writeQueue", DISPATCH_QUEUE_SERIAL);
NSCondition *readCondition = [[NSCondition alloc] init];
__block int writeQueueSize = 0;
const int maxWriteQueueSize = 10;
while (true) { // I'm assuming you have some way to break out of this...
NSCondition *writeCondition = [[NSCondition alloc] init];
// These need the __block attribute so they can be changed inside the blocks
__block BOOL frameProcessed = NO;
__block FrameType outputFrame = nil;
[readCondition lock];
while (writeQueueSize >= maxWriteQueueSize) {
[readCondition wait];
}
// video >> frame; // read a frame from the video
// dispatch the frame for processing
dispatch_async(backgroundQueue, ^{
[writeCondition lock];
processVideo(frame, outputFrame);
frameProcessed = YES;
[writeCondition signal];
[writeCondition unlock];
});
// dispatch the write
writeQueueSize++; // Increment the write queue size here, before the actual dispatch
dispatch_async(writeQueue, ^{
[writeCondition lock];
while (!frameProcessed) {
[writeCondition wait]; // this will block the current thread until it gets a signal
}
writeToVideo(outputFrame);
[writeCondition unlock];
// Decrement the write queue size and signal the readCondition that it changed
[readCondition lock];
writeQueueSize--;
[readCondition signal];
[readCondition unlock];
});
[readCondition unlock];
}
You can do this by giving each frame its own result queue, and chaining all the queues together in order. We suspend all the queues except the first one. Then as each frame finishes, it resumes the next result queue. This will force the queues to deliver their results in the order we want, regardless of when they complete their work.
Here's an example that just uses sleep to simulate some random amount of work and prints the results in the correct order. The dispatch_group is used here to prevent the program from exiting too soon. You might not need it in your case.
int main(int argc, const char * argv[])
{
#autoreleasepool {
dispatch_queue_t mainQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t myQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
for (unsigned x = 1; x <= 5; x++ ) {
// Chain the queues together in order; suspend all but the first.
dispatch_queue_t subQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(subQueue, myQueue);
dispatch_suspend(subQueue);
dispatch_group_async(group, mainQueue,^{
// Perform a random amount of work
u_int32_t sleepTime = arc4random_uniform(10);
NSLog(#"Sleeping for thread %d (%d)", x, sleepTime);
sleep(sleepTime);
// OK, done with our work, queue our printing, and tell the next guy he can print
dispatch_sync(myQueue, ^{
printf("%d ", x);
dispatch_resume(subQueue);
});
});
myQueue = subQueue;
}
// Wait for the whole group to finish before terminating
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
return 0;
}
I am trying to do an action AFTER the foreach and my dispatches. In the for loop when i come across one type of enum, the showAlert will get YES. For showing the allert after all the tasks and loops.
I've tried it, but the allert show up already when the dispatch_sync is still going. How can i solve this problem?
EDIT:
Problem overview: The problem is then that the UIAlertView already comes up before the loops are done, so i don't have any value set (For loop does this of my randomInt) and my AlertView takes this int. So That's why i want to wait until all loops/foreach are done in the method and then let the UIAlertView come up, so i have 100% the integer that i needed.
Code:
-(void)animateRolling{
[[self firstLabelOutlet] setHidden:YES];
long index;
int randomIndex;
__block BOOL showAllert = NO;
randomIndex = arc4random() % 20;
if (randomIndex == 8) {
[self showAd];
}
for(detailDice* cell in [[self dobbelstenenCollection] visibleCells]){
NSIndexPath *indexPath = [[self dobbelstenenCollection] indexPathForCell:cell];
if ([arrayDobbel count] >= 3) {
index = 2 * indexPath.section + indexPath.row;
}else{
index = 1 * indexPath.section + indexPath.row;
}
if ([cell isHidden]) {
[cell setHidden:NO];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i <= 10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
if ([[arrayDobbel objectAtIndex:index] diceSoort] == ENUMHoelangDobbel) {
randomInt = ((arc4random() % ([self maxduration] - 1 + 1 )) + 1.0);
[[cell detaildiceLabel] setText:[NSString stringWithFormat:#"Seconds \n %i", (int)randomInt]];
showAllert = YES;
}else if ([[arrayDobbel objectAtIndex:index] diceSoort] == ENUMOgen){
[[cell detailDiceImage] setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", ( (arc4random() % (6-1+1)) + 1 )]]];
}else{
[[cell detaildiceLabel] setText: [[arrayDobbel objectAtIndex:index] returnRandomOptie]];
}
});
[NSThread sleepForTimeInterval:0.05];
}
});
}
if (showAllert) {
[self showDurationAlert];
}
}
Kind Regards!
Create group with dispatch_group_create.
Use dispatch_group_async instead dispatch_async.
And path yours block, that should be performed after foreach and dispatch in dispatch_group_notify.
All is simple.
I would suggest to refactor your code through making animateRolling an asynchronous method, and the invocation of showDurationAlert a "continuation":
typedef void (^completion_t)(BOOL showAlert);
-(void)animateRollingWithCompletion:(completion_t)completion;
and use it as follows:
[self animateRollingWithCompletion:^(BOOL showAlert){
if (showAlert) {
dispatch_async(dispatch_get_main_queue(), ^{
[self showDurationAlert];
});
}
}];
I would also suggest to utilize a NSTimer or better a dispatch timer (dispatch_source_t) to implement the method animateRollingWithCompletion.
You can create and use dispatch timer as follows:
// Create a timer:
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// Define the handler block:
dispatch_source_set_event_handler(timer, ^{
...
});
// Start the timer (here, as a one shot timer):
uint64_t leeway = 0; // allowed maximum delay
uint64_t interval = delay_in_secs * NSEC_PER_SEC;
dispatch_source_set_timer(
timer,
dispatch_time(DISPATCH_TIME_NOW, interval), // time *when* to start
DISPATCH_TIME_FOREVER, // interval, DISPATCH_TIME_FOREVER for "one shot"
leeway // maximum allowed delay
);
dispatch_resume(timer);
// Cancel a timer:
dispatch_source_cancel(timer);
In order to start a periodic timer whose handler will be called at the start of each period, you might write:
dispatch_source_set_timer(
timer,
dispatch_time(DISPATCH_TIME_NOW, 0),
interval,
leeway);
dispatch_resume(timer);
With this knowledge, I think it's possible to craft a handler block, which is driven by a periodic timer. The block retrieves parameters form an array and executes your core methods (e.g. setting up the cells) on the main thread.
When all parameters have been retrieved and the work has been finished, the block may call dispatch_after() with the specified interval (or test for the "finished" condition at the start of a new period) which eventually calls the completion block provided as parameter in method animateRollingWithCompletion and defined by the call-site (which in turn brings up the Alert View).
I want to illustrate the progress on MBProgressHUD item, but when i triger this method :
- (IBAction)signInBttn:(id)sender {
MBProgressHUD *hudd = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hudd.mode = MBProgressHUDModeAnnularDeterminate;
hudd.labelText = #"Loading";
__block float value = 0;
for (int j = 0; j<2000; j++) {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i<20000 ; i++) {
}
value += 0.001;
dispatch_async( dispatch_get_main_queue(), ^{
hudd.progress = value;
});
});
}
}
hud appears fully to 100%. This is only for my information, I dont have idea how to create background task which calculate something and when he done with e.g. 40% the HUD is refreshing to 40% of his progress. I hope I made myself clear, and if anyone has time to help improve my code, thanks a lot for any answers
In this case, you can solve the problem by decoupling the updating of the counter from the updating of your HUD in your UI. Apple refers to this as "updating the state asynchronously" in WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC.
Generally this isn't necessary (most of the time the stuff we're doing asynchronously is slow enough that we don't have problems), but if doing something that is running faster than the UI can hope to keep up with, you create a "dispatch source" for this. I'm going to illustrate it with a UIProgressView, but the same applies to pretty much any UI:
// create source for which we'll be incrementing a counter,
// and tell it to run the event handler in the main loop
// (because we're going to be updating the UI)
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
// specify what you want the even handler to do (i.e. update the HUD or progress bar)
dispatch_source_set_event_handler(source, ^{
self.iterations += dispatch_source_get_data(source);
[self.progressView setProgress: (float) self.iterations / kMaxIterations];
});
// start the dispatch source
dispatch_resume(source);
// now, initiate the process that will update the source
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (long i = 0; i < kMaxIterations; i++)
{
// presumably, do something meaningful here
// now increment counter (and the event handler will take care of the UI)
dispatch_source_merge_data(source, 1);
}
// when all done, cancel the dispatch source
dispatch_source_cancel(source);
});
In my example, iterations is just a long property:
#property (nonatomic) long iterations;
And I defined my kMaxIterations constant as follows:
static long const kMaxIterations = 10000000l;
First off, if you want to delay execution use dispatch_after: Apple Doc since it could be that Clang is optimizing your loop (i.e. by making it not exist).
Within that block call dispatch_sync on the main thread to update the UI, since dispatch_async is not guaranteed to execute 'evenly'. Something like this ought to work...
for (...) {
dispatch_after(<some formula of i>, DEFAULT_PRIORITY, ^{
dispatch_sync(MAIN_QUEUE, ^{ hudd.progress = value });
}
}
I am trying to create a loop like this:
while (TRUE){
dispatch_after(...{
<some action>
});
}
After a viewDidLoad. The idea is to repeat the dispatch_after repeatedly. The dispatch_after waits two seconds before doing the action.
This does not work - the screen just blanks? Is it stuck in looping or ...?
Yes, you can do that with gcd. You need two additional c-functions though.
static void dispatch_async_repeated_internal(dispatch_time_t firstPopTime, double intervalInSeconds, dispatch_queue_t queue, void(^work)(BOOL *stop)) {
__block BOOL shouldStop = NO;
dispatch_time_t nextPopTime = dispatch_time(firstPopTime, (int64_t)(intervalInSeconds * NSEC_PER_SEC));
dispatch_after(nextPopTime, queue, ^{
work(&shouldStop);
if(!shouldStop) {
dispatch_async_repeated_internal(nextPopTime, intervalInSeconds, queue, work);
}
});
}
void dispatch_async_repeated(double intervalInSeconds, dispatch_queue_t queue, void(^work)(BOOL *stop)) {
dispatch_time_t firstPopTime = dispatch_time(DISPATCH_TIME_NOW, intervalInSeconds * NSEC_PER_SEC);
dispatch_async_repeated_internal(firstPopTime, intervalInSeconds, queue, work);
}
Tested! Works as intended.
https://gist.github.com/4676773
The dispatch_after(...) call returns immediately no matter when it is scheduled to run. This means that your loop is not waiting two seconds between dispatching them. Instead you are building an infinite queue of things that will happen two seconds from now, not two seconds between each other.
So yes, you are stuck in an infinite loop of adding more and more blocks to be executed. If you want something to happen every two second then you could use a repeating NSTimer or have the block dispatch_after inside itself (so that the second block runs two seconds after the first).
GCD already got this built in
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
https://gist.github.com/maicki/7622108
If you'd like an async task to run after a delay to check for example if a tag has been updated, then finish, you could use the code below:
typedef void (^RepeatCompletionHandler)(BOOL isRepeat);
typedef void (^RepeatBlock)(RepeatCompletionHandler completionHandler);
- (void)dispatchRepeat:(int)seconds withBlock:(RepeatBlock)block {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC),
dispatch_get_main_queue(), ^() {
block(^(BOOL isRepeat) {
if (isRepeat) {
return [self dispatchRepeat:seconds withBlock:block];
}
});
});
}
For example:
[self dispatchRepeat:5 withBlock:^(RepeatCompletionHandler completionHandler) {
[tagsService getTagValueForTagName:TagName value:^(NSString *tagValue) {
if (![TagValue isEqualToString:tagValue]) {
return completionHandler(YES);
}
completionHandler(NO);
}];
}];