vernou
vernou

Reputation: 7590

Confirm all tasks are finished before close the program

In my program, a lot of Task are started. This Task can start other tasks. But when the program is closed (end of Main method), all running tasks is stopped in middle of their work.

I need when the program is closed, the closing process wait all tasks. For this, I register all started tasks and in last instruction wait all register tasks :

public static class HostedTask
{
    private readonly static ConcurrentQueue<Task> _tasks = new ConcurrentQueue<Task>();

    public static void Run(Action action)
    {
        var task = Task.Factory.StartNew(action, TaskCreationOptions.LongRunning);
        _tasks.Enqueue(task);
    }

    public static void Wait()
    {
        while (_tasks.Any())
        {
            if (_tasks.TryDequeue(out Task task))
            {
                task.Wait();
            }
        }
    }
}
static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    for (int i = 0; i < 100; i+= 10)
    {
        LongBackgroundWork(i);
    }
    HostedTask.Wait();
}

static void LongBackgroundWork(int id)
{
    HostedTask.Run(() =>
    {
        Console.WriteLine(id + " Begin");
        Thread.Sleep(TimeSpan.FromSeconds(10));
        Console.WriteLine(id + " End");
        for (var i = id + 1; i < id + 10; i++)
            ChildWork(i);
    });
}

static void ChildWork(int id)
{
    HostedTask.Run(() =>
    {
        Console.WriteLine(id + " Begin");
        Thread.Sleep(TimeSpan.FromSeconds(2));
        Console.WriteLine(id + " End");
    });
}

This strategy have some problems :

Do you have other strategy/idea?

Edit : Complexify the example to work generate child work.

Upvotes: 1

Views: 1181

Answers (2)

vernou
vernou

Reputation: 7590

For the fun, the Foreground Task Scheduler. This scheduler will execute all task on a new foreground thread :

public class ForegroundTaskScheduler : TaskScheduler
{
    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return Enumerable.Empty<Task>();
    }

    protected override void QueueTask(Task task)
    {
        new Thread(() => base.TryExecuteTask(task)).Start();
    }

    protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        return false; //No inline
    }
}

public static class ForegroudTask
{
    public static TaskScheduler Scheduler { get; }
    public static TaskFactory Factory { get; }

    static ForegroudTask()
    {
        Scheduler = new ForegroundTaskScheduler();
        Factory = new TaskFactory(Scheduler);
    }
}

The use :

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
        for (int i = 0; i < 100; i += 10)
        {
            LongBackgroundWork(i);
        }
    }

    static void LongBackgroundWork(int id)
    {
        ForegroudTask.Factory.StartNew(() =>
        {
            Console.WriteLine(id + " Begin");
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine(id + " End");
        }, TaskCreationOptions.LongRunning).ContinueWith(t =>
        {
            for (var i = id + 1; i < id + 10; i++)
                ChildWork(i);
        }, ForegroudTask.Scheduler);
    }

    static void ChildWork(int id)
    {
        ForegroudTask.Factory.StartNew(() =>
        {
            Console.WriteLine(id + " Begin");
            Thread.Sleep(TimeSpan.FromSeconds(3));
            Console.WriteLine(id + " End");
        }, TaskCreationOptions.LongRunning);
    }
}

Just this don't work with async/await.

Upvotes: 0

Efthymios Kalyviotis
Efthymios Kalyviotis

Reputation: 969

Not sure of what exactly you are trying to do but maybe something like the bellow suits you better?

It ensures that every time a task is ended it is being removed by the list (a locked one) and also that you can wait for all tasks to end.

public static class HostedTask
{
    private readonly static List<Task> _tasks = new List<Task>();
    private static Object taskLocker = new object();

    public static async Task Run(Action action)
    {
        var task = Task.Factory.StartNew(action, TaskCreationOptions.LongRunning);

        lock (taskLocker)
            _tasks.Add(task);

        await task;

        lock (taskLocker)
            _tasks.Remove(task);
    }

    public static void Wait()
    {
        IEnumerable<Task> anys;
        do
        {
            lock (taskLocker)
            {
                anys = _tasks.Where(t => !t.IsCompleted);
            }

            if ((anys != null) && (anys.Count() > 0))
                Task.WhenAll(anys).Wait();
            else return;
        } while (true);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
        for (int i = 0; i < 100; i++)
        {
            LongBackgroundWork(i);
        }
        ShortBackgroundWork(-1);

        HostedTask.Wait();
    }

    static Task LongBackgroundWork(int id)
    {
        return HostedTask.Run(() =>
        {
            Console.WriteLine(id + " Begin");
            Thread.Sleep(TimeSpan.FromSeconds(10));
            Console.WriteLine(id + " End");
        });
    }

    static Task ShortBackgroundWork(int id)
    {
        return HostedTask.Run(() =>
        {
            Console.WriteLine(id + " Begin");
            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.WriteLine(id + " End");
        });
    }
}

Upvotes: 1

Related Questions