10

I am re-running a Task when its completed. Below is the function I call in the Application_Start of my application.

private void Run()
{
    Task t = new Task(() => new XyzServices().ProcessXyz());
    t.Start();
    t.ContinueWith((x) =>
    {
        Thread.Sleep(ConfigReader.CronReRunTimeInSeconds);
        Run();
    });
}

I want to run multiple tasks, number which will be read from web.config app setttings.

I am trying something like this,

private void Run()
{
    List<Task> tasks = new List<Task>();
    for (int i = 0; i < ConfigReader.ThreadCount - 1; i++)
    {
        tasks.Add(Task.Run(() => new XyzServices().ProcessXyz()));
    }

    Task.WhenAll(tasks);

    Run();
}

Whats the correct way to do this ?

Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281
  • if you don't want to dig into building your own taskrunner/extending that class I'd probably wait for the first task to end by using `tasks.First().Wait();` and then use a while loop with a Thread.Sleep to observe the status of the remaining tasks. – Steffen Winkler Jul 29 '15 at 07:54
  • 1
    You want to run a piece of code repeatedly in predetermined time intervals? Sounds like the exact reason `Timer` exists, really. And of course, if you want to stick with this, just bind your continuation to the result of `Task.WhenAll` and everything will work the same as before. – Luaan Jul 29 '15 at 08:27
  • You either need `await Task.WhenAll(tasks);` or `Task.WaitAll(tasks);`. One is a async call and should be awaited, the other is a normal function that can be called in a synchronous context. Of course you can also go for the ugly `Task.WhenAll(tasks).Wait();` – Dorus Jul 29 '15 at 12:49

3 Answers3

13

I believe you are looking for:

Task.WaitAll(tasks.ToArray());

https://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx

Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281
Mark Jansen
  • 1,491
  • 12
  • 24
9

if you want to run the tasks one after the other,

await Task.Run(() => new XyzServices().ProcessXyz());
await Task.Delay(ConfigReader.CronReRunTimeInSeconds * 1000);

if you want to run them concurrently, as the task scheduler permits,

await Task.WhenAll(new[]
    {
        Task.Run(() => new XyzServices().ProcessXyz()),
        Task.Run(() => new XyzServices().ProcessXyz())
    });

So, your method should be something like,

private async Task Run()
{
    var tasks =
        Enumerable.Range(0, ConfigReader.ThreadCount)
        .Select(i => Task.Run(() => new XyzServices().ProcessXyz()));

    await Task.WhenAll(tasks); 
}
Jodrell
  • 34,946
  • 5
  • 87
  • 124
  • I read earlier that await the tasks is not what starts their execution, that they actually start immediately or thereabouts. Is this correct? It would complicate your first statements. – D. Ben Knoble Jul 29 '15 at 14:12
  • If the functions you are calling are async themself, you do not need to wrap them in a `Task.Run`. However, it is important to realize a async task will start right away, on the current thread, and only switch context once it runs into a async call itself. For CPU heavy functions you should certainly wrap them in a `Task.Run`, for functions that quickly switch to a async call (usually IO), you get better performance by not wrapping them. – Dorus Jul 30 '15 at 10:04
1

If you want to wait all tasks to finish and then restart them, Marks's answer is correct.

But if you want ThreadCount tasks to be running at any time (start a new task as soon as any one of them ends), then

void Run()
{
    SemaphoreSlim sem = new SemaphoreSlim(ConfigReader.ThreadCount);

    Task.Run(() =>
    {
        while (true)
        {
            sem.Wait();
            Task.Run(() => { /*Your work*/  })
                .ContinueWith((t) => { sem.Release(); });
        }
    });
}
EZI
  • 15,209
  • 2
  • 27
  • 33
  • Why the inner `Task.Run`? You're taking up one thread blocking while another is used to run something synchronously. Just use `sem.Wait(); try { RunWhatever(); } finally { sem.Release(); }`. – Luaan Jul 29 '15 at 08:26
  • @Luaan No It is not what I want to do with my code. I am not waiting one task to finish then start a new one. I just create a new task whenever one of them finishes. So there will be always *ThreadCount* tasks running.. – EZI Jul 29 '15 at 08:32
  • Doesn't Tasks.WhenAll uses the ThreadPool which automatically runs the 'best' number of tasks at once, while queuing the others? – Roy T. Jul 29 '15 at 10:03
  • In a async context I would strongly prefer to use `WaitAsync()` on the semaphore. And there is no reason at all to not switch to a async context here. – Dorus Jul 29 '15 at 12:45
  • @RoyT. Not sure, but [Parallel.ForEach](https://msdn.microsoft.com/en-us/library/dd460720%28v=vs.110%29.aspx) would make so much more sense in that case. [TPL](https://msdn.microsoft.com/en-us/library/dd460717%28v=vs.110%29.aspx) is build around running tasks in parallel, no need to mix that with async methods like `Tasks.WhenAll` unless your work is async. – Dorus Jul 29 '15 at 13:06