【问题标题】:C# Cancel a list of tasks which are executing long running sql queriesC#取消正在执行长时间运行的sql查询的任务列表
【发布时间】:2017-08-18 22:33:02
【问题描述】:

等待时间到期后,我需要取消正在运行 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);
    }

【问题讨论】:

  • 这取决于你的代码,你用什么来执行查询?有时可以将 cancelToken 作为参数传递给异步方法。请记住:在一定时间后取消 SQL 查询最好是通过将超时(毫秒)传递到执行查询的位置(而不是取消令牌)。如果您不知道何时停止(例如用户想要停止它),您只会使用取消令牌。
  • Maerlin,我添加了一些示例代码。我已经有单独的 sql 查询超时,但是我正在使用信号量小批量运行任务,所以说每个查询在 10 秒后超时,并且有 10 个这样的批次,相当于 100 秒运行时,我想在 10 秒后取消所有任务的执行。
  • 阅读Stephen Cleary的优秀博客,里面有很多关于异步操作的文章

标签: c# asp.net parallel-processing task-parallel-library cancellation-token


【解决方案1】:

您需要在作为 Action 实例传递给 EnqueueTask 方法的函数中使用 CancellationToken

例如以下代码展示了如何使用 CancellationToken 来终止 SQL 命令的执行:

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

        using (cancellationToken.Register(() => cmd.Cancel()))
        {
            cmd.CommandText = sql;
            cmd.ExecuteNonQuery();
        }
    }

【讨论】:

  • 您需要在该 cancelToken.Register 周围放置一个 using 块,该块在 ExecuteNonQuery 之后立即关闭
  • 感谢@Tim,已修复。
  • u 显示cancellationToken,但不显示它属于哪个类或函数
【解决方案2】:

编写一个使用新线程启动查询的任务,该任务可以继续检查您的 CancelationToken 的状态并在需要时终止线程。

例如,您可以扩展 Task 并添加此功能。

【讨论】:

  • r1verside,这也解决了我的问题,但我选择 Olexiy 的答案,因为它看起来更简单。谢谢你。不能给你+1,因为我没有足够的代表:|
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-16
  • 1970-01-01
相关资源
最近更新 更多