Search code examples
objective-cgrand-central-dispatchdispatch-async

Implement a Job Scheduler using Dispatch Queues


Question: Implement a job scheduler which takes in function f and int n and calls f after n seconds.

Since im using Obj C to answer this question, I should probably use dispatch queues instead of selectors or function pointers.

Here's my attempt to implement the answer, but it's not returning a time stamp:

void jobSch(dispatch_queue_t queue, int64_t n, dispatch_block_t f)
{
    NSLog(@"%d\n", n);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), dispatch_get_main_queue(), f);
}

void (^aBlock)(void) = ^(){
    NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSLog(@"%@",[dateFormatter stringFromDate:[NSDate date]]);
};

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_async(group, queue, ^{
            jobSch(queue, 3, aBlock);
        });
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    }
    return 0;
}

What could I be doing wrong?

Also my intended implementation would be able to call jobSch(queue, 10, aBlock) at runtime 0 and jobSch(queue, 3, aBlock) at runtime 2, so that would mean the second call would print before the first.


Solution

  • The problem is that main is exiting long before your enqueued block is run. And that is because your call to dispatch_group_wait isn't waiting. And that is because your dispatch group is empty.

    In order for a dispatch group and a call to dispatch_group_wait to be useful, you need to make paired calls to dispatch_group_enter and dispatch_group_leave.

    You need to call dispatch_group_enter before you start any async call and you need to call dispatch_group_leave after the block has finished (aBlock in this case).

    Your code is not setup to make this easy. You would need to make group a global variable. Then you can call dispatch_group_enter and dispatch_group_leave where needed.

    Something like this should work:

    dispatch_group_t group;
    
    void jobSch(dispatch_queue_t queue, int64_t n, dispatch_block_t f)
    {
        NSLog(@"%d\n", n);
        dispatch_group_enter(group);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), queue, f);
    }
    
    void (^aBlock)(void) = ^(){
        NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        NSLog(@"%@",[dateFormatter stringFromDate:[NSDate date]]);
        dispatch_group_leave(group);
    };
    
    int main(int argc, const char * argv[])
    {
        @autoreleasepool {
            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            group = dispatch_group_create();
    
            // no need for dispatch_group_async here
            jobSch(queue, 10, aBlock);
            jobSch(queue, 3, aBlock);
    
            dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        }
        return 0;
    }
    

    Also note that you never actually make use of queue inside jobSch.

    You need to change:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), dispatch_get_main_queue(), f);
    

    to:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, n * NSEC_PER_SEC), queue, f);