【问题标题】:Task cancellation based on Task ID根据任务 ID 取消任务
【发布时间】:2019-07-25 14:28:28
【问题描述】:

.Net 4.5

我有一个任务列表

List<Task> lTasks

每个任务都有自己的

“CancellationTokenSource”和“cancellationToken”

Task.Id 和 Task.status

在 DataGridView 中列出。

DataGridView 中的每一行还有一个名为“StopTask”的按钮。

如果用户点击了“StopTask”按钮,该特定任务应该被取消

任务.Id

我该怎么做?

这是我每次调用时都会创建一个新任务的方法

 private void CreateNewTask(int RowIndex)
        {
            CancellationTokenSource cts = new CancellationTokenSource(); //Init new CancellationTokenSource
            var cancellationToken = cts.Token;
            try
            {
                var t = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("Start new task");
                    for (int i = 0; i < 100; i++)
                    {
                        dataGridView_TaskList.Rows[RowIndex].Cells["OutputValue1"].Value = i;
                        System.Threading.Thread.Sleep(1000);
                    }

                    cancellationToken.ThrowIfCancellationRequested();

                }, cancellationToken);

                lTasks.Add(t); //Add to Liast of tasks (List<Task>)

                dataGridView_TaskList.Rows[RowIndex].Cells["TaskID"].Value = t.Id;
                dataGridView_TaskList.Rows[RowIndex].Cells["TaskStatus"].Value = t.Status;
            }
            finally
            {
                cts.Dispose();
            }
        }

UI with DataGridView


根据答案和 cmets,我做了以下操作,看起来效果很好。

我使用 DataGridView 作为我的“字典”并将每个任务的令牌保存在

dataGridView_TaskList.Rows[RowIndex].Cells["RowIndex"].Tag = cts; 

我也用过

行索引

作为唯一 ID,因为两个任务可能具有相同的 TaskID。

新建任务如下图所示:

 private async void CreateNewTask(int RowIndex)
        {
            CancellationTokenSource cts = new CancellationTokenSource(); //Init new CancellationTokenSource
            var cancellationToken = cts.Token;
            try
            {
                Task t = Task.Run(async () => //Task.Run automatically unwraps nested Task types!
                {
                    try
                    {
                        Console.WriteLine("Start new task");
                        for (int i = 0; i < 100; i++)
                        {
                            // inside this loop the Token cancellation has no effect, it will keep running until this loop is finished then it will throw a cancellation exception
                            dataGridView_TaskList.Rows[RowIndex].Cells["OutputValue1"].Value = i;
                            await Task.Delay(300);
                        }

                        cancellationToken.ThrowIfCancellationRequested();
                    }
                    catch (OperationCanceledException ex)
                    {
                        ShowMsgBox.Show(ex.Message, "Cancelation", enumMessageIcon.Information, enumMessageButton.OK);
                    }

                }, cancellationToken);

                lTasks.Add(t); //Add to Liast of tasks (List<Task>)

                dataGridView_TaskList.Rows[RowIndex].Cells["TaskID"].Value = t.Id; //The TaskId is not guaranteed to be unique
                dataGridView_TaskList.Rows[RowIndex].Cells["TaskStatus"].Value = t.Status;
                dataGridView_TaskList.Rows[RowIndex].Cells["RowIndex"].Value = RowIndex;
                dataGridView_TaskList.Rows[RowIndex].Cells["RowIndex"].Tag = cts;


            }
            catch (Exception ex)
            {
                ShowMsgBox.Show(ex.Message, "Exception", enumMessageIcon.Error, enumMessageButton.OK);
            }
        }

可以取消任务,如下图所示

   private void dataGridView_TaskList_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            var senderGrid = (DataGridView)sender;

            if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn &&
                e.RowIndex >= 0)
            {
                if (senderGrid.Columns[e.ColumnIndex].Name is "StopTask")
                {
                    //TODO - Button Clicked - Execute Code Here
                    int.TryParse(dataGridView_TaskList.Rows[e.RowIndex].Cells["RowIndex"].Value.ToString(), out int RowIndex);
                    CancellationTokenSource cts_ToBeCancelled = (CancellationTokenSource)dataGridView_TaskList.Rows[e.RowIndex].Cells["RowIndex"].Tag;
                    cts_ToBeCancelled.Cancel();
                }
            }
        }

【问题讨论】:

  • cts 放到 Rows 的标签...
  • 好主意,我会试试的
  • Id 不保证是唯一的,因此请勿将其用于此类目的。
  • Guid 也不能保证是唯一的,也许可以为您的警告提供上下文或范围?
  • @NineBerry 是对的,两个任务可能具有相同的 TaskID。因此,我使用 DataGridView 中的 RowIndex 作为唯一 ID

标签: c# cancellationtokensource cancellation-token


【解决方案1】:

将带有取消令牌源的任务 id 添加到字典中,您可以从那里通过 id 提取取消令牌。

Dictionary&lt;int,CancellationTokenSource&gt; taskLookup = new Dictionary&lt;int,CancellationTokenSource&gt;();

...

var t = Task.Factory.StartNew(() =&gt; ... taskLookup.Add(t.Id, cts);

另外,您不能在处理 CancellationTokenSource 之后再使用它来取消。

【讨论】:

  • 字典在类级别,而不是在方法中。
  • 如果任务完成,我不需要取消它,因此我正在处理 ,,,CancellationTokenSource ,,,
  • 任务不一定在你处理它的时候完成。
  • 但是在 dispose 的那一刻,你的任务已经启动了,但是还不太可能完成。
【解决方案2】:

您需要致电cancellationToken.Cancel(); 取消任务。

但是,在您的任务中,您需要检查它是否被取消。这可能意味着在你的 for 循环中你应该添加以下内容:

if (ct.IsCancellationRequested)
{
    // Clean up here, then...
    ct.ThrowIfCancellationRequested();
}

请注意,任务中的 Thread.Sleep 是一个坏主意,因为这会阻塞来自线程池的线程。请改用await Task.Delay

【讨论】:

  • System.Threading.Thread.Sleep(1000);仅用于测试目的
【解决方案3】:

我遇到了同样的问题,为了解决这个问题,我从 Task 类驱动并添加并识别到 MyTask 类,并在构造中注入 GUID 以区分任务。

public class MyTask : Task
{
    public Guid Identifire;

    public MyTask(Action action, Guid identifire) : base(action)
    {
        Identifire = identifire;
    }

    public MyTask(Action action, CancellationToken cancellationToken, Guid identifire) : base(action, cancellationToken)
    {
        Identifire = identifire;
    }

    public MyTask(Action action, TaskCreationOptions creationOptions, Guid identifire) : base(action, creationOptions)
    {
        Identifire = identifire;
    }

    public MyTask(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions, Guid identifire) : base(action, cancellationToken, creationOptions)
    {
        Identifire = identifire;
    }

    public MyTask(Action<object> action, object state, Guid identifire) : base(action, state)
    {
        Identifire = identifire;
    }

    public MyTask(Action<object> action, object state, CancellationToken cancellationToken, Guid identifire) : base(action, state, cancellationToken)
    {
        Identifire = identifire;
    }

    public MyTask(Action<object> action, object state, TaskCreationOptions creationOptions, Guid identifire) : base(action, state, creationOptions)
    {
        Identifire = identifire;
    }

    public MyTask(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, Guid identifire) : base(action, state, cancellationToken, creationOptions)
    {
        Identifire = identifire;
    }


}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 2017-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多