-1

I am trying to send a keep-alive HTTP request each 30 minutes. However I have other methods to be called at the time in between, so what I tried to do is:

Task.Factory.StartNew(async () =>
{
    while(true){
        await Task.Delay(new TimeSpan(0,30,0), CancellationToken.None);
        await FooTask();
    }
});

Am I using it properly?

Julian
  • 5,290
  • 1
  • 17
  • 40

4 Answers4

1

Are you doing it properly? No. You say you want a loop, but don't write a loop. You're also using the wrong task creation function:

Task.Run(async () =>
{
    while(true)
    {
        await FooTask().ConfigureAwait(false);
        await Task.Delay(new TimeSpan(0, 30, 0)).ConfigureAwait(false);
    }
});

There's also PeriodicTimer which follows a similar pattern, you do your action and await the next tick.

Blindy
  • 65,249
  • 10
  • 91
  • 131
0

I'd suggest using Microsoft's Reactive Framework.

Then you can just do this:

IDisposable subscription =
    Observable
        .Timer(TimeSpan.Zero, TimeSpan.FromMinutes(30.0))
        .SelectMany(_ => Observable.FromAsync(() => FooTask()))
        .Subscribe();

Calling subscription.Dispose() shuts it down. It's nice and simple.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

My understanding is that Task.Run() is effectively fire and forget.

If you try to 'wait' for a Task.Run() to complete it will return almost immediately because it's 'task' is to launch the inner operation. Once it's done that it's IsCompleted. It does not hook onto the inner operation.

So suppose you have 3 long-running sub-tasks, JobA(), JobB() and JobC(), to be completed sequentially...

public bool ExecuteLongRunningTasks(CancellationToken cancelToken = default)
{
    if (!_JobACompleted)
    {
        JobA();
        _JobACompleted = true;

        if (cancelToken.IsCancellationRequested)
            return false;
    }

    if (!_JobBCompleted)
    {
        JobB();
        _JobBCompleted = true;

        if (cancelToken.IsCancellationRequested)
            return false;
    }

    JobC();
    _AllJobsCompleted = true;

    return true;
}

... and that at at some point the UI thread may require the results of these 3 sub-tasks (which have been launched asynchronously), then it stands to reason that you will allow a long running sub-task to complete asynchronously, and then have the better performing UI thread complete the uncompleted sub-tasks.

To achieve this I have to use StartNew() for the asynchronous operations because StartNew(), 'by default', hooks onto the operation it is launching...

public bool AsyncExecute()
{
    // Property accessor that does a few sanity checks.
    if (!ClearToLoadAsync)
        return false;

    _AsyncActive = true;

    _AsyncTask = Task.Factory.StartNew<bool>(
        () => 
        {
            ExecuteLongRunningTasks(_CancelToken);
            _AsyncActive = false;
            return true;
        },
        default, TaskCreationOptions.LongRunning, TaskScheduler.Default);

    return true;
}

... and if the UI thread needs the results it uses the much maligned Wait()...

public bool SyncExecute()
{
    // Just a property accessor that does some trivial deadlock and completion checks.
    if (!ClearToLoadSync)
        return false;

    if (_AsyncActive)
    {
       // Post a cancel request and wait.
        _CancelTokenSource.Cancel();
        _AsyncTask.Wait();
    }

    if (!_AllJobsCompleted)
    {
       // Execute any outstanding long running tasks synchronously.
       return ExecuteLongRunningTasks();
    }

    return true;
}

If anyone has a solution that achieves the same with Task.Run() I would love to see how it is done because I was unsuccessful.

Greg C
  • 11
  • 2
-1

You likely want to use Task.Run() instead of Task.Factory.StartNewAsync(), per the remarks in the docs. Task.Run() is recommended unless you need the extra options in Task.Factory.StartNewAsync() (ex: Task Creation Options, Task Scheduler, Parameter Passing that Task.Run() does not cover).

Task.Run() will queue the task to run on the current thread pool, and it will also return a handle to the task in case you want to Wait for completion or Cancel the task, as shown in the below example.

CancellationTokenSource src = new CancellationTokenSource();
CancellationToken ct = src.Token;
Task t = Task.Run(async () =>
{
    while(true){
        ct.ThrowIfCancellationRequested();
        await Task.Delay(new TimeSpan(0,30,0), ct);
        await FooTask();
    }
}, ct);


// Do some other stuff while your task runs

// Cancel Your Task, OR
ct.Cancel();

// Wait for your task to finish
try
{
    t.Wait();
}
catch (AggregateException e)
{
    // Handle the exception
}
tkawchak
  • 24
  • 3