【问题标题】:Run tasks in parallel and some of them consecutively并行运行任务,其中一些连续运行
【发布时间】:2017-10-05 05:49:52
【问题描述】:

我想并行运行方法 A 和方法 B1。这是有效的。但是如何在 B1 完成后运行方法 B2?

    class Program
    {
        static void Main(string[] args)
        {
            //var firstTask = Task.Factory.StartNew(() => MethodB1());
            //var secondTask = firstTask.ContinueWith( (antecedent) => MethodB2());

            Action[] actionsArray =
            {
                () => MethodA(),
                () => MethodB1(),
            };

            Parallel.Invoke(actionsArray);
        }

        private static void MethodA()
        {
            Console.WriteLine("A");
            // more code is running here (30 min)
        }

        private static void MethodB1()
        {
            Console.WriteLine("B1");
            // more code is running here (2 min)
        }

        private static void MethodB2()
        {
            Console.WriteLine("B2");
        }
    }

编辑: 我希望下面的例子能停止混淆。 ;)

A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A
B1 -> B1 -> B1 -> B1 -> B1 -> B2 -> B2 -> B2

【问题讨论】:

  • 您是否考虑过来自 Tasks 的异步和等待?
  • 任务 task = new Task(doWork);任务.开始();任务 newTask = task.ContinueWith(doMoreWork);像这样的东西也应该有效,对吧?
  • 你的方法是做什么的?他们是执行一些繁重的计算还是访问一些外部资源(数据库、Web 服务、文件系统......)?
  • @Fabio 请查看我的编辑。我与一些物联网设备进行通信并进行分析。
  • 我刚刚了解到,在 C# 7.1 中,您现在可以在 main 中使用异步!

标签: c# task task-parallel-library


【解决方案1】:

C# 是一种很好的语言来做到这一点,有很多方法可以做到这一点,正如评论所暗示的,另一种是这样的:

static void Main()
{
    var t = MethodA();
    MethodB1().ContinueWith((r) =>
    MethodB2()).Wait();
    t.Wait();
}

private static async Task MethodA()
{
    await Task.Run(() =>
    {
        for (int i = 0; i < 40; i++)
        {
            Thread.Sleep(100);
            Console.WriteLine("A");
        }
    });
}

private static async Task MethodB1()
{
    await Task.Run(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Thread.Sleep(100);
            Console.WriteLine("B1");
        }
    });
}

private static void MethodB2()
{
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(100);
        Console.WriteLine("B2");
    }
}

【讨论】:

  • OP 的方法不是异步的,因此您需要将它们包装在任务中,但将它们包装在任务中并不总是最好的方法,例如在方法访问外部资源的情况下
  • @KirillShlenskiy 快速的眼睛。固定。
  • @Fabio 你不必这样做。你可以让它异步。如果有充分的理由不可能做到这一点,那么您已经解决了无法进行异步处理这一事实的原因,这不是您通过包装在 Task Factory 中来解决的问题。
  • 你可以等待一个返回Task的方法,而不是void,我想
  • 看来B1是在A完成后才开始的。但我需要 A 和 B1 并行运行。
【解决方案2】:

您可以使用ContinueWith 轻松实现此目的。这是代码

 public static void Main(string[] args)
 {
     var t1 = Task.Factory.StartNew(MethodA);           
     var t2 = Task.Factory.StartNew(MethodB1).ContinueWith(task => MethodB2());   
     Task.WaitAll(t1, t2); // If you want to wait here for finishing the above tasks                                           
 }

要描述输出,您可以尝试使用MethodAMethodB1MethodB2 的以下实现

private static void MethodA()
{
    for (int i = 0; i < 100; i++)
    {
        Console.Write("A ");
        Thread.Sleep(100);
    }  
}

private static void MethodB1()
{

    for (int i = 0; i < 100; i++)
    {
        Console.Write("B1 ");
        Thread.Sleep(100);
    }
}

private static void MethodB2()
{
    for (int i = 0; i < 100; i++)
    {
        Console.Write("B2 ");
        Thread.Sleep(100);
    }
}

【讨论】:

    【解决方案3】:

    这不适用于 VS2017 和更新版本,因为在 VS2017 之前的控制台应用程序主干上不支持异步关键字。在旧版本上,您应该使用 Task.WhenAll().Wait(); 而不是 await Task.WhenAll()

    namespace ConsoleApp3
    {
        class Program
        {
            static async void Main(string[] args)
            {
                var taskA = Task.Run(() => MethodA()); //Start runnning taskA
                var taskB1AndB2 = Task.Run(() => MethodB1()).ContinueWith(async (taskb1) => { await taskb1; await Task.Run(()=>MethodB2()); }).Unwrap(); //When taskB1 is complete, continue with taskB2
                await Task.WhenAll(taskA, taskB1AndB2); //wait until all 3 tasks are completed
            }
    
            private static void MethodA()
            {
                Console.WriteLine("A");
                // more code is running here (30 min)
            }
    
            private static void MethodB1()
            {
                Console.WriteLine("B1");
                // more code is running here (2 min)
            }
    
            private static void MethodB2()
            {
                Console.WriteLine("B2");
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      一种方法是委托回调机制类程序。另一个可以是基于事件的 这里是 Used First 方法。给出时间样本

      这正是你想要的

      class Program
      {
      
          static System.Collections.Concurrent.ConcurrentQueue<Action> leftOvers = new System.Collections.Concurrent.ConcurrentQueue<Action>();
          static void Main(string[] args)
          {
      
      
              for (int i = 0; i < 100; i++)
              {
                  Task.WaitAll(MethodA(), MethodB1(MethodB2));
              }
      
              Action callBack = null;
              while (leftOvers.TryDequeue(out callBack))
              {
                  callBack();
              }
              Console.ReadLine();
          }
      
          private static void QueueMethods(Action method)
          {
              leftOvers.Enqueue(method);
          }
      
          private async static Task MethodA()
          {
              await Task.Run(() => Console.WriteLine("A  at" +  DateTime.Now.TimeOfDay ));
      
          }
      
      
          private async static Task MethodB1(Action callBack)
          {
              await Task.Run(() => Console.WriteLine("B1 at" + DateTime.Now.TimeOfDay));
              leftOvers.Enqueue(callBack);
      
          }
      
          private  static void MethodB2()
          {
              Console.WriteLine("B2 at" + DateTime.Now.TimeOfDay);
      
          }
      }
      

      【讨论】:

      • B2 只能在 B1 完成后运行。
      • 看 B2 我打电话给 Inside B1。所以没有一点 B2 之前会打电话。另外因为我使用的是循环。所以它是如此之快,所以任何 B1 或 A1 将首先执行,但 B2 肯定会在 B1 之后运行。尝试成对地看 B1 和 B2
      • 请看我的编辑。 B1 可能运行 10 次,然后 B2 运行 10 次。也许这不是很清楚? (英语不是我的母语。)
      【解决方案5】:

      只需在 Parallel.Invoke 之后添加 B2。 Parallel.Invoke 在转到下一行之前等待完成。 https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-use-parallel-invoke-to-execute-parallel-operations

          public static void Main(string[] args)
          {
              Action[] actionsArray =
              {
                  () => MethodA(),
                  () => MethodB1()
              };
      
              Parallel.Invoke(actionsArray);
              MethodB2();
          }
      
          private static void MethodA()
          {
              Console.WriteLine("A");
              Thread.Sleep(3000);
              // more code is running here (30 min)
          }
      
          private static void MethodB1()
          {
              Console.WriteLine("B1");
              Thread.Sleep(200);
              // more code is running here (2 min)
          }
      
          private static void MethodB2()
          {
               Console.WriteLine("B2");
          }
      

      【讨论】:

      • 你知道,也许我不应该!
      • 我明白你的意思了。
      • 这里我有一个问题,B2 仅在 A 完成后运行。但是我需要 B2 直接在 B1 之后运行!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 2011-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      相关资源
      最近更新 更多