2

so in my program I want the first thread that finishes first's value to be displayed in console and then have it so that all remaining threads are then aborted. Here is the code that I have now that demonstrates the issue.

    public static void test()
    {
        int Loop = 0;

        Parallel.For(0, 2000, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, (i, End) =>
        {
            Loop++;
            Console.WriteLine(Loop);

            if (Loop == 1000)
            {
                Console.WriteLine("Break!");
                End.Break();
            }
        });
    }

And when it's done here is a snippet of what gets outputted into the console

985
987
983
Break!
Break!
992
998
Break!
Break!
00:00:00.7217394

So right now I have no idea how to get it to stop all together when one thread matches the if statement. Any help would be greatly appreciated!

jazb
  • 5,498
  • 6
  • 37
  • 44
  • 1
    `Loop` is not thread safe, there is no guarantee your `WriteLines` are going to print in the order you think, the test program is nonsensical, and all these facts makes exceedingly hard to help you. – TheGeneral Nov 02 '18 at 05:06
  • @TheGeneral sorry, i'm having a hard time trying to explaining this. – ArcticWolf_11 Nov 02 '18 at 07:24

2 Answers2

4

Parallel.Forxxx support CancellationTokens. Just create a CancellationTokenSource, assign it to an instance of ParallelOptions and during your loop, you can just trigger it when desired.

public static void test()
{
    int Loop = 0;

    CancellationTokenSource cts = new CancellationTokenSource();

    try
    {
        Parallel.For(0, 2000, 
              new ParallelOptions() 
              { 
                  MaxDegreeOfParallelism = Environment.ProcessorCount, 
                 CancellationToken = cts.Token 
              }, 
        (i, End) =>
        {
            cts.Token.ThrowIfCancellationRequested();

            // ...

            if (i == 1000) // you should be using 'i'
            {
                Console.WriteLine("Break!");
                cts.Cancel();
            }
        });
    }
    catch (OperationCanceledException )
    {
        // expected behaviour
    }
}

As TheGeneral said, your loop isn't thread safe so I have made some changes and left a space for you to fill.

Tell me More

  • Isn't `End.Stop()` will do the same? – vasily.sib Nov 02 '18 at 05:19
  • @vasily.sib I thought it would but with End.Stop() it'll keep going until all threads hit that if statement. – ArcticWolf_11 Nov 02 '18 at 05:21
  • @vasily.sib I'm not sure. Reading the doco gives the impression that it will process a batch first or something. I've always used the above method –  Nov 02 '18 at 05:23
  • @MickyD Thanks but when I add `Loop++;` and `Console.WriteLine(Loop);` in the middle where the `// ...` is I end up with a single break outputted to the console but the Parallel.For loop is still going, it's still outputting loop's value into the console. – ArcticWolf_11 Nov 02 '18 at 05:51
  • @MickyD yeah I have another question in mind to ask instead that would fit the situation better. I don't really use stackoverflow very much so should I delete this question before starting a new one then? – ArcticWolf_11 Nov 02 '18 at 06:01
  • @ArcticWolf_11 - Keep the question as it is perfectly fine. – Enigmativity Nov 02 '18 at 06:03
  • @ArcticWolf_11 no don't delete the question. The reason why you are still seeing output in the form of `Console` is because it is somewhat buffered and their order is not guaranteed with respect to your operations (if multiple threads are all spamming console writes it will be difficult to determine the order). You are most likely seeing the output of another thread prior to it checking to see if it should cancel –  Nov 02 '18 at 06:50
  • @MickyD I see, but if one of the threads hits the if statement and says `Break!`, shouldn't that make everything stop at that point in time similar to Thread.Abort();? – ArcticWolf_11 Nov 02 '18 at 07:30
4

Another option is to use ParallelLoopState.Stop() method. I have tested it with empty Console application:

Parallel.For(0, 2000, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
    (i, state) =>
    {
        Console.Write($"{i}, ");

        if (i == 1000)
        {
            Console.Write("STOP! ");
            state.Stop();
        }
    });

The untruncated output was:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 1000, STOP! 580, 1500, 16,

Almost same result as with CancellationToken, except you don't have to handle OperationCanceledException.

There is also a ParallelLoopState.Break() method. Which is slightly different. It stops execution of all future iterations of loop. So in this case, you will not see any numbers more then 1000 after STOP! text, but they still may be present before it.

vasily.sib
  • 3,871
  • 2
  • 23
  • 26
  • Interesting. Would be interesting to know when it aborts the other threads? Immediate? What happens with partitioning? Hard to tell from `Console.Write`xxx. Maybe log it out somewhere else using another means with datetime? +1 –  Nov 02 '18 at 05:53
  • @vasily.sib I just tried `Stop()` instead of `Break()` and they both did the exact same thing, it still kept on going sadly. – ArcticWolf_11 Nov 02 '18 at 05:55
  • 1
    @MickyD - This wouldn't abort any threads in the `.Abort()` sense. – Enigmativity Nov 02 '18 at 06:00
  • @MickyD as far as I know, it stops before next iteration, so if one thread is invoking `state.Stop()` all other threads runs to the end of their current iterations (thats why you can see `580, 1500, 16,` after `STOP!`). – vasily.sib Nov 02 '18 at 06:00
  • @ArcticWolf_11 replace `if (Loop == 1000)` with `if (i == 1000)` in your code – vasily.sib Nov 02 '18 at 06:01
  • @ArcticWolf_11 - This code works fine. You've just chosen `1000` as your stopping point which is one of the last values processed. Try it with `10`. – Enigmativity Nov 02 '18 at 06:02
  • @vasily.sib Yeah my bad. – ArcticWolf_11 Nov 02 '18 at 06:27
  • @Enigmativity I did that because what if say you had to scan something like a text file and you needed to break once you hit a certain like or something? I was trying to simulate something like that. – ArcticWolf_11 Nov 02 '18 at 06:27
  • @Enigmativity do you know anything that does the same thing that .Abort() does that I could use here? – ArcticWolf_11 Nov 02 '18 at 07:35
  • @ArcticWolf_11 - You should never ever use `.Abort()` unless you are trying to forcibly exit your app. `.Abort()` can leave the .NET run-time in an undefined state and thus corrupting your code in the other threads that are still running. – Enigmativity Nov 02 '18 at 07:37
  • @ArcticWolf_11 - Try using Microsoft's Reactive Framework (Nuget "System.Reactive") then you can do this: `Observable.Range(0, 2000).SelectMany(x => Observable.Start(() => new { index = x, result = LongRunningMethod(x) })).TakeUntil(x => x.index == 1000).Subscribe(x => { /* output results */ });`. – Enigmativity Nov 02 '18 at 07:41
  • @Enigmativity well... I didn't know that, how does it corrupt code? Also thanks, i'll take a look at that and do you have any other suggestions? I'd rather not have to use nuget packages. – ArcticWolf_11 Nov 02 '18 at 09:05
  • @ArcticWolf_11 - https://stackoverflow.com/questions/1559255/whats-wrong-with-using-thread-abort – Enigmativity Nov 03 '18 at 09:12