none
Parallel and synchronous - how to launch multiples of same command at the exact same moment?

    Question

  • I'm going to make the example as simple as possible:

    I want a program that executes Hello World 10 times at the exact same moment in the Console Window. I don't care if the Console Window is not fast enough, if it is less than ten times because of that, then it is. My actual application does something completely different, I'm just using Console Window as a simple example.

    Anyway, I do know the basics of how to use "Parallel For Each". The only problem that has is that it is way too slow with the initialization. The actual code running is easily fast enough, but I can't wait for several seconds for it to launch the separate commands. I am ok with it waiting between initialization and first, but not with first and nth command. I have exactly one command I am running and I want it to happen within a couple of milliseconds of each other.

    So if I execute this at 0:00:00.000, then if the first execution takes place at 0:00:00.000 and second takes place at 0:00:01.000 - that is not good enough, not even if then final one would be done at 0:00:00:01.001. That just doesn't cut it.

    But, if I execute this at 0:00:00.000, then if the first execution takes place at 0:01:00.000 (a minute later) and second takes place at 0:01:00.005 - that is good enough. I just want them at the same time.

    So should I go with something like 

    DateTime waitfrom = DateTime.Now;
    DateTime waituntil = new DateTime(waitfrom.Year, waitfrom.Month, waitfrom.Day, waitfrom.Hour, waitfrom.Minute + 1, waitfrom.Second);
    
    Parallel.ForEach(collection, collectionmember =>
    {
     double durationUntilNextMinute = (waituntil - DateTime.Now).TotalMilliseconds;
     var t = new Timer(o => { Console.WriteLine("Hello World!"); }, null, (uint)durationUntilNextMinute, 0);
    });

    or do you have a better option? Right now it seems for some reason to skip over the Parallel.ForEach altogether. And yeah, I definitely won't require using Parallel For Each - you say something else is better, it probably is.

    edit. Oh yeah, I have tried normal "For Each". That is not fast enough. For "Hello World" it is, but the execution of that one single command I have takes too much time. It is not important when then execution is finished. Only the start point matters.

    • Edited by ArtKilp Wednesday, March 29, 2017 10:38 PM
    Wednesday, March 29, 2017 10:32 PM

Answers

  • In order to perform some actions at specified time or after a delay, consider “waitable timers” too. There are various articles and samples. Here is a simplified experiment, which starts four threads that print “Hello, world!” after 15 seconds:

    DateTime waitfrom = DateTime.Now;
    DateTime waituntil = waitfrom.AddSeconds( 15 );
    
    long due_time = waituntil.ToFileTimeUtc();
    
    IntPtr waitable_timer = CreateWaitableTimer( IntPtr.Zero, true, null );
    SetWaitableTimer( waitable_timer, ref due_time, 0, IntPtr.Zero, IntPtr.Zero, false );
    
    EventWaitHandle wait_handle = new EventWaitHandle( false, EventResetMode.ManualReset ) { SafeWaitHandle = new SafeWaitHandle( waitable_timer, false ) };
    
    int number_of_threads = 4;
    Thread[] threads = new Thread[number_of_threads];
    
    for( int i = 0; i < number_of_threads; ++i )
    {
    	int n = i;
    	threads[i] = new Thread( () =>
    	{
    		wait_handle.WaitOne();
    		Console.WriteLine( $"Hello, world! Thread: {n}" );
    	} );
    	threads[i].Start();
    }
    
    foreach( var t in threads ) t.Join();

    Also add to the class:

    [DllImport( "kernel32" )]
    static extern IntPtr CreateWaitableTimer( IntPtr attributes, bool isManualReset, string name );
    
    [DllImport( "kernel32" )]
    static extern bool SetWaitableTimer( IntPtr timer, ref long dueTime, int period, IntPtr completionRoutine, IntPtr arg, bool resume );



    • Edited by Viorel_MVP Thursday, March 30, 2017 7:57 AM
    • Marked as answer by ArtKilp Thursday, March 30, 2017 8:25 PM
    Thursday, March 30, 2017 7:55 AM

All replies

  • Strange requirement, but:

            public static void TimedTest()
            {
                DateTime waitfrom = DateTime.Now;
                Task[] t = new Task[2];
                t[0] = Task.Run(() =>
                {
                    Thread.Sleep(1000);
                });
                t[1] = Task.Run(() =>
                {
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine("Hello World!");
                    }
                });
                Task.WaitAll(t);
                Console.WriteLine("Time elapsed: {0}", (DateTime.Now - waitfrom).TotalMilliseconds);
            }

    The result is either 1000 or 1010 on my machine. Note that Windows is not a real time OS so by your requirement you can adjust the time to "Sleep()" so the difference is no longer significant. The result could also be varied if you run it on notebook or machine that have power management because "step down"-ed CPU cycle means the time slice of Windows' scheduler becomes bigger.



    Thursday, March 30, 2017 1:36 AM
    Answerer
  • In order to perform some actions at specified time or after a delay, consider “waitable timers” too. There are various articles and samples. Here is a simplified experiment, which starts four threads that print “Hello, world!” after 15 seconds:

    DateTime waitfrom = DateTime.Now;
    DateTime waituntil = waitfrom.AddSeconds( 15 );
    
    long due_time = waituntil.ToFileTimeUtc();
    
    IntPtr waitable_timer = CreateWaitableTimer( IntPtr.Zero, true, null );
    SetWaitableTimer( waitable_timer, ref due_time, 0, IntPtr.Zero, IntPtr.Zero, false );
    
    EventWaitHandle wait_handle = new EventWaitHandle( false, EventResetMode.ManualReset ) { SafeWaitHandle = new SafeWaitHandle( waitable_timer, false ) };
    
    int number_of_threads = 4;
    Thread[] threads = new Thread[number_of_threads];
    
    for( int i = 0; i < number_of_threads; ++i )
    {
    	int n = i;
    	threads[i] = new Thread( () =>
    	{
    		wait_handle.WaitOne();
    		Console.WriteLine( $"Hello, world! Thread: {n}" );
    	} );
    	threads[i].Start();
    }
    
    foreach( var t in threads ) t.Join();

    Also add to the class:

    [DllImport( "kernel32" )]
    static extern IntPtr CreateWaitableTimer( IntPtr attributes, bool isManualReset, string name );
    
    [DllImport( "kernel32" )]
    static extern bool SetWaitableTimer( IntPtr timer, ref long dueTime, int period, IntPtr completionRoutine, IntPtr arg, bool resume );



    • Edited by Viorel_MVP Thursday, March 30, 2017 7:57 AM
    • Marked as answer by ArtKilp Thursday, March 30, 2017 8:25 PM
    Thursday, March 30, 2017 7:55 AM