-1

I want to run method A and method B1 in parallel. This is working. But how can I run a method B2 after B1 has finished?

    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");
        }
    }

Edit: I hope the following example will stop the confusion. ;)

A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A -> A
B1 -> B1 -> B1 -> B1 -> B1 -> B2 -> B2 -> B2
kame
  • 20,848
  • 33
  • 104
  • 159
  • 2
    have you considered async and await from Tasks? – tofutim Oct 05 '17 at 05:51
  • Task task = new Task(doWork); task.Start(); Task newTask = task.ContinueWith(doMoreWork); Something like this should also work, right? – kame Oct 05 '17 at 05:53
  • What your methods do? Do they execute some heavy calculations or they access some external resources (database, web service, file system...)? – Fabio Oct 05 '17 at 05:53
  • @Fabio please see my edit. I communicate with some IoT devices and make an analysis. – kame Oct 05 '17 at 05:56
  • I just learned that in C# 7.1 you can now have async in main! – tofutim Oct 05 '17 at 05:57
  • You don't have to make main async. The one I have mentioned in my answer is the guideline of microsoft Azure code used in many Azure open source codes like `DotNetty` – Emad Oct 05 '17 at 06:00
  • check [this](https://stackoverflow.com/questions/38634376/running-async-methods-in-parallel) – Ferus7 Oct 05 '17 at 06:02
  • What you want to do is called blocking. To block you use semaphores. In c# I recommend using WaitOne for the semaphores. See msdn examples : https://msdn.microsoft.com/en-us/library/58195swd(v=vs.110).aspx and https://learn.microsoft.com/en-us/dotnet/framework/network-programming/asynchronous-server-socket-example – jdweng Oct 05 '17 at 06:06
  • `async-await` can be used in C#4 too, see [Microsoft.Bcl.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async). – Fabio Oct 05 '17 at 06:30
  • I recommend to use the [TPL Dataflow](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library) library. – Larry Oct 05 '17 at 07:47
  • @kame I updated the answer – Ramankingdom Oct 05 '17 at 10:45

5 Answers5

1

C# is a great language to do this and there are many ways to do it one is as the comment suggests another is this:

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");
    }
}
Emad
  • 3,809
  • 3
  • 32
  • 44
  • OP's methods are not asynchronous, so you need wrap them in tasks, but wrapping in tasks not always best approach, for example in case methods accessing external resources – Fabio Oct 05 '17 at 05:56
  • @KirillShlenskiy Quick eyes. Fixed. – Emad Oct 05 '17 at 05:57
  • @Fabio You don not have to. You can just make it async. If it's impossible with a good reason then you have fix the reason the fact that you can't make something async is not something you fix by wrapping in Task Factory. – Emad Oct 05 '17 at 05:58
  • You can await a method that returns `Task`, instead of void, I think – Ferus7 Oct 05 '17 at 06:02
  • It looks like B1 is only started after A finished. But I need A and B1 running parallel. – kame Oct 05 '17 at 06:16
  • No it's not. Since you don't `await` A it will be fire and forget. It will start and program counter passes through it. Then B1 starts but program counter waits for it to finish and then B2 starts. – Emad Oct 05 '17 at 06:17
  • By running synchronous methods in parallel you kind of wrapping them with Task/thread. – Fabio Oct 05 '17 at 06:20
  • @Fabio That's automatically done with language features when using `async/await` if you use `Task.Factory` you have to manage many things like cancellation token, thread pools, completion and exception. So that's for when you want more control this is for when you just want parallel work. – Emad Oct 05 '17 at 06:23
  • @kame I'm sooo sorry man. You are completely right I forgot two basic things. First the one you saw when you let a task run without getting the handler it will become sync. And two was that if B2 was done before A program would exit before A was done. I fixed both. This is completely working and tested. :) – Emad Oct 05 '17 at 06:41
  • Now A will stop running when B2 has finished. https://pastebin.com/6YsbDDKa – kame Oct 05 '17 at 06:51
  • Add this line in the end of Main `t.Wait();` – Emad Oct 05 '17 at 06:55
1

You can easily achieve this by using ContinueWith. Here is the code

 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                                           
 }

To depict the output you can try with the following implementation of MethodA, MethodB1 and MethodB2

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);
    }
}
Noor A Shuvo
  • 2,639
  • 3
  • 23
  • 48
0

This will not work with VS2017 and newer because async keyword is not supported on console app mains before VS2017. On older version, instead of await Task.WhenAll() you should do Task.WhenAll().Wait();

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");
        }
    }
}
Gerrie Pretorius
  • 3,381
  • 2
  • 31
  • 34
0

One way is Delegate CallBack Mechanism class Program. The another can be event based Here is Used First approach . Given with time samples

This is exactly what you want

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);

    }
}
Ramankingdom
  • 1,478
  • 2
  • 12
  • 17
  • B2 should only run after B1 has finished. – kame Oct 05 '17 at 07:46
  • Look B2 I called Inside B1. So there is not point that B2 will call before. Also since I am using loop . so its so fast so Any B1 or A1 will execute first but B2 will surely run after B1. Try to see in pairs of B1 and B2 – Ramankingdom Oct 05 '17 at 09:45
  • 1
    Please have look at my edit. B1 is running maybe 10 times and then B2 is running 10 times. Maybe this wasn't clear? (English is not my mother tongue.) – kame Oct 05 '17 at 10:32
-1

Just add B2 after Parallel.Invoke. Parallel.Invoke waits to finish before going to the next line. https://learn.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");
    }
tofutim
  • 22,664
  • 20
  • 87
  • 148