Search code examples
c#windowsmultithreadingservicetask-parallel-library

Cancelling a task from a list of tasks


I have a requirement to poll database and create parquet file for each table. This tasks are to run in parallel with some configurable limit on the number of threads to be created.
Once the threads are added and the tasks starts to run I need to set a timer so that if any query take more than some specified time can cancel the task and close that thread. If anymore task is there in the collection then the next thread will be called.
I am using CancellationTokenSource but it is not working as expected.

public async static Task CreateExtractionAndUpload(ConfigurationDetails config)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    cts.CancelAfter(10000);
    
    List<Task> getData = new List<Task>();
    foreach (var query in config.Root.Queries)
    {
        getData.Add(Task.Run(() => DoExtractionFromQueries(query, dbManager, cts.Token)));
    }

     await Task.WhenAll(getData);
}

private async static void DoExtractionFromQueries(ExtractionQueries query, DBManager dBManager, CancellationToken cancelToken)
{
    try
    {
        while (!cancelToken.IsCancellationRequested)
        {
            Thread.Sleep(20000);
            var dataTable = dBManager.GetDataTable(query.Query, System.Data.CommandType.Text);
            //ParaquetFileHandler.GenerateParquetFile(dataTable);
        }
    }
    catch (TimeoutException ex)
    {
        Logger.Error("Query taking longer than expected time!", ex);
    }
    catch (Exception ex)
    {
        Logger.Error("Exception in running query!", ex);
    }
}

What I am doing wrong and how to rectify it?
How can I limit the number of threads?
I can limit the threads in Parallel.Foreach but can I cancel a task after timeout?


Solution

  • I am using CancellationTokenSource but it is not working as expected.

    Cancellation in .NET is cooperative. So the code must check whether it is canceled. If it doesn't check, it's not canceled.

    In this case, you'll probably need to modify dBManager.GetDataTable to make it cancel-aware: add a CancellationToken parameter to that method, and pass it to any long-running methods that it calls.