【问题标题】:Task.ContinueWith does not call 2nd task (only first one is running)Task.ContinueWith 不调用第二个任务(只有第一个正在运行)
【发布时间】:2014-11-11 09:26:21
【问题描述】:

我的应用程序允许用户从 UI 创建“任务”对象并设置它们的执行顺序。从那里,当用户单击“运行”时,所有任务(按顺序)都被发送到一个假设链接并运行它们的类。类如下:

public class MaintenanceTask
{
    private CancellationTokenSource m_CancellationTokenSource;
    private Task m_BatchTask;

    public MaintenanceTask()
    {            
        m_CancellationTokenSource = new CancellationTokenSource();            
    }

    public void AddTaskIdent(TaskIdentBase ident)
    {
        Task newTask = ident.Create(m_CancellationTokenSource.Token);

        if (m_BatchTask == null)
            m_BatchTask = newTask;            
        else                
            m_BatchTask.ContinueWith(prev=>newTask);                        
    }

    public void Run()
    {
        if (m_BatchTask != null)
            m_BatchTask.Start();
    }
}

AddTaskIdent 正在接收一个知道如何创建任务对象的基类。 我的问题是调用 Run() 启动发送给班级的第一个任务。它运行它,直到它运行后没有其他任务完成。 它只是在第一个任务完成后停止。

我是否缺少 ContinueWith 机制的内容?

【问题讨论】:

  • 什么是TaskIdentBase
  • 只是我系统中的一个类,它知道什么以及如何创建不同的任务。它的 Create() 返回想要的 Task 对象。
  • 是的...实现很重要...但是我认为您没有正确使用Task...

标签: c# .net multithreading asynchronous


【解决方案1】:

问题出在这一行:

m_BatchTask.ContinueWith(prev=>newTask);

我认为newTask 在从ident.Create 返回时没有启动。 所以你应该:

if (m_BatchTask != null)
{
    m_BatchTask = newTask;
    //you'd like to have the m_lastTask field because next chaining operation
    //should be on the last task, not the one you are starting
    m_lastTask = newTask;
}
else
{
    m_lastTask.ContinueWith(
        prev =>
        {
            newTask.Start();
            //doesn't matter to return newTask because in that case
            //on second chaining already you will get Task<Task>
            return (object)null; 
        });
    m_lastTask = newTask;
}

【讨论】:

    【解决方案2】:

    在合并所有答案的数据并继续谷歌搜索后,终于让它工作了。 工人阶级是:

    public class MaintenanceTask
    {
        private CancellationTokenSource m_CancellationTokenSource;
        private Task m_FirstTask;
        private Task m_LastTask;
    
        public MaintenanceTask()
        {
            m_CancellationTokenSource = new CancellationTokenSource();
        }
    
        public void AddTaskIdent(TaskIdentBase ident)
        {
            Task newTask = ident.Create(m_CancellationTokenSource.Token);
    
            if (m_FirstTask == null)
            {
                m_FirstTask = newTask;
                m_LastTask = m_FirstTask;
            }
            else
            {                
                m_LastTask.ContinueWith(prev => newTask.Start());
                m_LastTask = newTask;
            }
        }
    
        public void Run()
        {
            if (m_FirstTask != null)
                m_FirstTask.Start();
        }
    }
    

    【讨论】:

      【解决方案3】:

      注意“foo.ContinueWith(completedTask => otherTask);”不启动其他任务。 你需要类似“foo.ContinueWith(completedTask => otherTask.Start());”的东西

      在调试器中试一试,这可能有助于您了解其工作原理:

              static void Main(string[] args)
              {
                  Task task1 = new Task(() => Console.WriteLine("1"));
                  Task task2 = new Task(() => Console.WriteLine("2"));
                  Task task3 = new Task(() => Console.WriteLine("3"));
      
                  task1.ContinueWith(previous => task2.Start());
                  task2.ContinueWith(previous => task3.Start());
      
                  task1.Start();
                  task2.Wait();
                  Console.WriteLine("Done.");
              }
      

      【讨论】:

      • 只尝试了 2 个任务,但仍然无法正常工作。另外,将代码更改为看起来像您的代码,但不起作用..我的 actionA/B/C 是 Task 对象..
      • 我认为“我的行为是任务对象”实际上阐明了问题所在。等一下……
      • 我把答案的前半部分扔掉了,因为它是错误的。关键是ContinueWith 采用的是Action,而不是Task。在您的情况下,您只需将该操作启动为任务。我实际上不确定为什么你的问题中的代码甚至可以编译 - 当然,它显然可以编译,但在我看来它不应该。我要学点东西了.... :-)
      • 刚刚试过了。添加了 .Start() 到那时它没有顺序..所有任务都一起运行。我猜你的测试正在工作,因为 writeline() 不需要时间:\
      • 在第二个示例中(我只是为了避免混淆而将其删除)我只是说明 ContinueWith 维护一个列表,而不是单个下一个任务。当第一个任务完成时会调用所有延续,因此如果您想要排序,您需要将任务链接在一起,如第一个(仅现在)示例中所示。很抱歉没有更清楚地说明这一点。
      【解决方案4】:

      你有没有用

      检查过 else 分支
        else                
              m_BatchTask.ContinueWith(prev=>newTask);       
      

      被调用?

      正如我所见,当他将下一个任务应用于它时,m_Batch 任务最初为空,但没有引发继续

      【讨论】:

      • 它被调用。当我添加第一个任务时,它不再为空。第二个任务转到else部分
      【解决方案5】:

      说实话,我认为您错误地使用了Task。在大多数情况下,Task 代表已经在进行的工作。

      假设 C#5,这就是我编写您“想要”的代码的方式。

      public void AddToQueue(Action action)
      {
          if (m_BatchTask == null)
              m_BatchTask = Task.Run(action);            
          else                
              m_BatchTask = AddToQueue(m_BatchTask, action);                        
      }
      
      public async Task AddToQueue(Task existingTask, Action action)
      {
          await existingTask;
          action();                       
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        • 2016-10-02
        • 1970-01-01
        • 2018-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多