-1

I need to call the same method in parallel in C# (MaxDegreeOfParallelism = Environment.ProcessorCount). The method returns a sequence of numbers. If this sequence fulfills certain criteria - I need to do the following

  1. Stop all threads
  2. Return the sequence
  3. Return how many time the method has been called in total

I have the below code - but I have some problems figuring out what to write in my GetValidSequenze method where I have written: //WHAT TO DO HERE??

Any ideas? Am I doing something wrong?

public class Example3
{
    public delegate int Randomizer(int minValue, int maxValue);

    private Randomizer rand;

    public Example3(Randomizer randomizer)
    {
        rand = randomizer;

    }
    private IEnumerable<bool> Infinite()
    {
        while (true)
        {
            yield return true;
        }
    }

    public int[][] GetValidSequenze(int minValue, int maxValue, int rows,
        int columns, int sn, ref int counter)
    {
        ParallelOptions op = new ParallelOptions();
        op.MaxDegreeOfParallelism = Environment.ProcessorCount;
        int[][] result;

        Parallel.ForEach(Infinite(), parallelOptions: op //WHAT TO DO HERE?? =>
        {
            int[][] tempRes;
            while (!(tempRes = GetSequenze(minValue, maxValue, rows, columns))
                .All(o => o.Contains(sn)))
            {
                Interlocked.Increment(ref counter);
            }
            loopState.Stop();
            result = tempRes;
        });

        return result;
    }

    public int[][] GetSequenze(int minValue, int maxValue, int rows, int columns)
    {
        int[][] lot = new int[rows][];
        for (int i = 0; i < rows; i++)
        {
            int[] column = new int[columns];
            for (int j = 0; j < columns; j++)
            {
                while (true)
                {
                    int tempNo = rand(0, 40);
                    if (!column.Contains(tempNo))
                    {
                        column[j] = tempNo;
                        break;
                    }
                }
            }
            lot[i] = column;
        }
        return lot;
    }
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Peter
  • 89
  • 2
  • 10
  • 1
    What if columns is 42? – Selvin Feb 02 '21 at 18:32
  • I don't understand what you're asking. https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions.maxdegreeofparallelism `"Generally, you do not need to modify this setting."` MSDN points out that the system should handle `MaxDegreeOfParallelism` automatically. – Nate W Feb 02 '21 at 18:44
  • It is for an assignment that have specified I should set MaxDegreeOfParallelism to the processor count. – Peter Feb 02 '21 at 20:40
  • @NateW actually the advice given by the docs is not great. If you don't specify explicitly the `MaxDegreeOfParallelism`, it will keep its default value `-1`, which means unbounded parallelism. This will cause the `ThreadPool` to become immediately saturated, and to stay saturated until the source enumerable completes. This is bad news in case you also have other concurrent operations happening in parallel, like for example a `System.Timers.Timer`: its `Elapsed` event will start firing hectically and sporadically. This is a poor behavior, and is not imitated by the subsequent PLINQ library. – Theodor Zoulias Feb 02 '21 at 22:16
  • @TheodorZoulias Interesting. I didn't know that. I see your point that there are circumstances where one would wish to limit this, beyond what the documentation points out. Any articles / SO q's for the benefit of others here that would point me in the direction of further reading on what you're talking about? – Nate W Feb 03 '21 at 17:57
  • @NateW I don't know of any articles about this subject. What I know is from personal experimentation. You could read two relevant discussions in the comments of two questions [here](https://stackoverflow.com/questions/17785049/parallel-for-partitioning) (with Panagiotis Kanavos) and [here](https://stackoverflow.com/questions/65900144/parallel-loop-containing-both-async-and-synchronous/65902423#65902423) (with Stephen Cleary). I am really puzzled about how this presumably problematic behavior has passed largely unnoticed. – Theodor Zoulias Feb 03 '21 at 18:33

2 Answers2

1

Your code doesn't compile. Below is version of the GetValidSequenze method that compiles:

public int[][] GetValidSequenze(int minValue, int maxValue, int rows,
    int columns, int sn, ref int counter)
{
    int localCounter = counter;
    ParallelOptions op = new ParallelOptions();
    op.MaxDegreeOfParallelism = Environment.ProcessorCount;
    int[][] result = default;

    Parallel.ForEach(Infinite(), parallelOptions: op, (_, loopState) =>
    {
        int[][] tempRes;
        while (!(tempRes = GetSequenze(minValue, maxValue, rows, columns))
            .All(o => o.Contains(sn)))
        {
            Interlocked.Increment(ref localCounter);
        }
        loopState.Stop();
        result = tempRes;
    });

    counter = localCounter;
    return result;
}

Chances are that whatever you want to do, could be done more efficiently by improving the algorithm, than by parallelizing a probably inefficient algorithm.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
-1

Define a variable that you plan to set when your criteria are met.

bool criteriaMet = false;

Set it when they are met.

Modify your enumerator:

private IEnumerable<bool> Infinite()
{
    while (!criteriaMet)
    {
        yield return true;
    }
}

If you find that the Infinite() method doesn't detect any change in criteriaMet, it might be due to a compiler optimization. If that is the case you may need to use the volatile keyword, or (better) use a CancellationToken instead of a bool.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • The `Parallel.ForEach` by default employs chunk partitioning, meaning that each worker thread progressively takes more and more elements from the source enumerable. So your suggestion to stop the loop by terminating the enumerable will most probably result to delayed termination of the parallel loop. To disable the chunk partitioning you need to provide a somewhat awkward partitioner: `Partitioner.Create(Infinite(), EnumerablePartitionerOptions.NoBuffering)`. – Theodor Zoulias Feb 02 '21 at 22:03