C # Отменить список задач, выполняющих длительные запросы sql

Мне нужно отменить список задач, выполняющих SQL-запросы по истечении времени ожидания. Я могу реализовать CancellationToken для отмены задач. Но отмена является кооперативной, поэтому это означает, что я должен проверять статус токена отмены внутри своего действия перед каждым шагом. Но в моем случае запросы sql - это те, которые занимают много времени, и я могу проверить статус токена отмены только до или после выполнения запроса. В последнем случае это бесполезно, так как мне отменить выполнение запроса внутри этих задач на основе статуса токена отмены?

public void EnqueueTask(Action action, CancellationToken cancelToken = default(CancellationToken))
{
    var task = new Task(action, cancelToken, TaskCreationOptions.LongRunning);
    if (_workTaskQueue.TryAdd(task))
    {
        TaskHandler?.Invoke
            (new TaskProcessingArguments
            {
                ISTaskAdded = true,
                Message = "Task Added to Queue",
                PendingTaskCount = _workTaskQueue.Count,
            });
    }
    else
    {
        TaskHandler?.Invoke
            (new TaskProcessingArguments
            {
                ISTaskAdded = false,
                Message = "Timedout while adding Task to Queue",
                PendingTaskCount = _workTaskQueue.Count,
            });
    }
}

public void DequeueTask(int maxConcurrency, CancellationToken ct)
{
    var tasks = new List<Task>();
    using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
    {
        foreach (var task in _workTaskQueue.GetConsumingEnumerable())
        {
            try
            {
                if (!(task.IsCanceled) && task.Status == TaskStatus.Created)
                {
                    tasks.Add(task);
                    task.Start();
                }
            }
            finally {
                concurrencySemaphore.Release();
            }
        }
    }
    Task.WaitAll(tasks.ToArray());
}

    void StartWorker()
    {
        Task.Factory.StartNew(() =>
        {
            try
            {
                taskQueue.DequeueTask(maxConcurrency, cancellationToken);
            }
            finally {
                lock (syncObj)
                {
                    IsCompleted = true;
                }
                //Logger.Info("Closing Worker task!!!");
            }
        }, TaskCreationOptions.LongRunning);
    }

person Nitheesh    schedule 18.08.2017    source источник
comment
это зависит от вашего кода, что вы используете для выполнения запросов? Иногда можно передать cancellationToken асинхронному методу в качестве параметра. Помните: для отмены SQL-запроса через определенное время лучше передать тайм-аут (в миллисекундах), откуда выполняется запрос (а не токен отмены). Вы можете использовать токен отмены только в том случае, если не знаете, когда остановиться (например, пользователь хочет остановить его.)   -  person Benjamin Schäublin    schedule 19.08.2017
comment
Maerlin, я добавил образец кода. У меня уже есть индивидуальный тайм-аут для sql-запроса, но я запускаю задачи небольшими партиями, используя семафор, поэтому скажем, что каждый запрос в пакете истекает через 10 секунд, и есть 10 таких пакетов, что равно 100 секундам время выполнения. Вместо этого я хочу отменить выполнение всех задач через 10 секунд.   -  person Nitheesh    schedule 19.08.2017
comment
Прочтите отличный блог Стивена Клири, в котором есть много статей об асинхронных операциях.   -  person Sir Rufo    schedule 19.08.2017


Ответы (2)


Вам необходимо использовать CancellationToken внутри функций, переданных как экземпляры Action методу EnqueueTask.

Например. следующий код показывает, как CancellationToken можно использовать для прекращения выполнения команд SQL:

    using (SqlConnection conn = new SqlConnection(sqlConnection))
    {
        conn.Open();
        var cmd = conn.CreateCommand();

        using (cancellationToken.Register(() => cmd.Cancel()))
        {
            cmd.CommandText = sql;
            cmd.ExecuteNonQuery();
        }
    }
person Olexiy Sadovnikov    schedule 18.08.2017
comment
Вы захотите поместить блок using вокруг этого cancellationToken.Register, который закрывается сразу после ExecuteNonQuery - person Tim; 19.08.2017
comment
Спасибо @Tim, исправлено. - person Olexiy Sadovnikov; 19.08.2017
comment
u Показать cancellationToken, но не показать, к какому классу или функции он принадлежит - person csharpdudeni77; 08.01.2020

Напишите задачу, которая использует новый поток для запуска запроса, задача может продолжить проверку состояния вашего CancelationToken и при необходимости убить поток.

Вы можете расширить Task ‹> и, например, добавить эту функциональность.

person Pablo Recalde    schedule 18.08.2017
comment
r1verside, это тоже решило мою проблему, но я иду с ответом Алексея, поскольку он кажется более простым. Спасибо. Не могу дать вам +1, потому что у меня мало представителей: | - person Nitheesh; 19.08.2017