代码之家  ›  专栏  ›  技术社区  ›  Ian Vink

任务.WaitAny()检查结果

  •  1
  • Ian Vink  · 技术社区  · 6 年前

    我希望能够并行地运行所有任务,一旦第一个任务返回“Good”,则取消其他任务并获得“Good”结果。

    我现在正在这样做,但问题是,所有的任务都需要运行,然后我在它们之间循环寻找第一个好的结果。

    List<Task<string>> tasks = new List<Task<string>>();
    Task.WaitAll(tasks.ToArray());
    
    4 回复  |  直到 6 年前
        1
  •  2
  •   Scott Chamberlain    6 年前

    这是误会,因为 Cancellation in TPL is co-operative ,因此一旦任务启动,就无法取消它。 CancellationToken 如果请求取消,则可以在任务启动之前或之后抛出异常,这意味着启动并采取必要的操作,如从逻辑中抛出自定义异常

    检查以下各项 query

    public static class TaskExtension<T>
    {
      public static async Task<T> FirstSuccess(IEnumerable<Task<T>> tasks, T goodResult)
    
        {
            // Create a List<Task<T>>
            var taskList = new List<Task<T>>(tasks);
            // Placeholder for the First Completed Task
            Task<T> firstCompleted = default(Task<T>);
            // Looping till the Tasks are available in the List
            while (taskList.Count > 0)
            {
                // Fetch first completed Task
                var currentCompleted = await Task.WhenAny(taskList);
    
                // Compare Condition
                if (currentCompleted.Status == TaskStatus.RanToCompletion
                    && currentCompleted.Result.Equals(goodResult))
                {
                    // Assign Task and Clear List
                    firstCompleted = currentCompleted;
                    break;
                }
                else
                   // Remove the Current Task
                   taskList.Remove(currentCompleted);
            }
            return (firstCompleted != default(Task<T>)) ? firstCompleted.Result : default(T);
        }
    }
    

    var t1 = new Task<string>(()=>"bad");
    
    var t2 = new Task<string>(()=>"bad");
    
    var t3 = new Task<string>(()=>"good");
    
    var t4 = new Task<string>(()=>"good");
    
    var taskArray = new []{t1,t2,t3,t4};
    
    foreach(var tt in taskArray)
      tt.Start();
    
    var finalTask = TaskExtension<string>.FirstSuccess(taskArray,"good");
    
    Console.WriteLine(finalTask.Result);
    

    你甚至可以回来 Task<Task<T>> ,而不是 Task<T> 进行必要的逻辑处理

        2
  •  1
  •   Muhammad Hannan    6 年前

    List<Task<string>> tasks = new List<Task<string>>();  
    
    // ***Use ToList to execute the query and start the tasks.   
    List<Task<string>> goodBadTasks = tasks.ToList();  
    
    // ***Add a loop to process the tasks one at a time until none remain.  
    while (goodBadTasks.Count > 0)  
    {  
        // Identify the first task that completes.  
        Task<string> firstFinishedTask = await Task.WhenAny(goodBadTasks);  
    
        // ***Remove the selected task from the list so that you don't  
        // process it more than once.  
        goodBadTasks.Remove(firstFinishedTask);  
    
        // Await the completed task.  
        string firstFinishedTaskResult = await firstFinishedTask;  
        if(firstFinishedTaskResult.Equals("good")
             // do something
    
    }  
    

    有关更多详细信息,请阅读 docs

        3
  •  1
  •   Sailing Judo    6 年前

    我在调查 Task.WhenAny()

    虽然我不认为你的问题有一个满意的答案,但我认为你的问题可能有另一种解决办法。考虑使用 Parallel.ForEach .

            Parallel.ForEach(tasks, (task, state) =>
            {
                if (task.Result != null)
                    state.Stop();
            });
    

    这个 state.Stop()

    除了有能力在发现“好”值时停止执行之外,它在许多(但不是所有)场景下都会表现得更好。

        4
  •  0
  •   Poul Bak    6 年前

    使用 Task.WhenAny

    如果它是好的,取消列表中的所有任务(它们都应该有一个 CancellationTokenSource.Token .

    编辑:

    所有任务都应使用相同的 可以cellationTokenSource.令牌 . 那你只需要取消一次。 下面是一些需要澄清的代码:

    private async void button1_Click(object sender, EventArgs e)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    
        List<Task<string>> tasks = new List<Task<string>>();
        tasks.Add(Task.Run<string>(() => // run your tasks
           {
               while (true)
               {
                   if (cancellationTokenSource.Token.IsCancellationRequested)
                   {
                       return null;
                   }
                   return "Result";  //string or null
               }
           }));
        while (tasks.Count > 0)
        {
            Task<string> resultTask = await Task.WhenAny(tasks);
            string result = await resultTask;
            if (result == null)
            {
                tasks.Remove(resultTask);
            }
            else
            {
                // success
                cancellationTokenSource.Cancel(); // will cancel all tasks
            }
        }
    }