0

I'm trying to convert my single thread function into a multithreaded function. I've been reading up on threading, but I'm unsure of the proper structure needed to be able to start a thread for each server in the array while at the same time waiting for the threads to finish and receiving a return value from each one before being able to parse the return value data.

So the order of operations should be

  1. Start a new thread for each server name
  2. Each thread when it ends should receive an output from the function runPowerShellScript(server))
  3. Wait for each thread to end and then organize the data. If I have 5 servers, I will have 5 different variables with return values

Also, how does the OS/compiler handle a situation like this where the return variable (returnVal) name is the same with each thread opened? I understand this is probably basic for someone who was classroom taught, since I am self taught I'm not sure what to do.

    private void Run_Click(object sender, EventArgs e)
    {
        string[] servers = { "server1", "server2", "server3", "server4", "server5" };

        foreach (string server in servers)
        {
            Collection<PSObject> returnVal = new Collection<PSObject>();
            Thread psquery = new Thread(() => returnVal = runPowerShellScript(server)); // lambda expression assigns return value to function
            psquery.Start();
            psquery.Join(); // waits for thread to finish
        }

        // process data here
    }
Bbb
  • 517
  • 6
  • 27
  • 1
    You need an array of return values. Consider using Task instead, Task.WaitAll() will be easier. You need an array of Tasks. – Hans Passant Aug 28 '18 at 15:42

2 Answers2

1

You could use the TAP pattern (https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap). Since you want to wait for each task to finish you can have the following approach:

private async void Run_Click(object sender, EventArgs e)
{
     string[] servers = { "server1", "server2", "server3", "server4", "server5" };

     var returnVal = new List<PSObject>();

     foreach (var server in servers)
     {
        var result = await Task.Run(() => runPowerShellScript(server));
        returnVal.Add(result);
     }

     // process data here
}

Each task will be awaited for the result and added to the returnVal listso you can use the result in your code.

Teler
  • 477
  • 6
  • 15
0

There is an article for further reading about your options on Stackoverflow here: article

As for a code example using your question/examples, using the PTL API:

    /// <summary>
    /// I didn't test this, I am assuming you can work out the details
    /// (I prefer this way)
    /// </summary>
    /// <returns></returns>
    private void Run_Click(object sender, EventArgs e)
    {
        var response = new ConcurrentBag<PSObject>();
        var exceptions = new ConcurrentQueue<Exception>();

        string[] servers = { "server1", "server2", "server3", "server4", "server5" };

        try
        {
            Parallel.ForEach(servers, server =>
            {
                response.Add(runPowerShellScript(server));
            });
        }
        catch (AggregateException ae)
        {
            foreach (var e in ae.InnerExceptions)
            {
                exceptions.Enqueue(e);
            }
        }

        if (exceptions.ToList().Any())
        {
            //there are exceptions, do something with them
            //do something?
        }

        try
        {
            // quote:  // process data here
        }
        catch (Exception e)
        {
            // do something with it
        }
    }
Epistaxis
  • 201
  • 1
  • 6
  • This worked excellently, thank you! Are there any other speed tricks I can do with this? (I understand my function may need optimizing as well) – Bbb Aug 28 '18 at 18:33
  • Well, sorry, I am unclear as to what you need/want as speedy, so I can't suggest anything there, sorry. – Epistaxis Aug 28 '18 at 18:39
  • This way, if the first server causes an `AggregateException`, none of the other servers are going to be processed, you might want to stick the `try-catch` inside `Parallel.Foreach` – Ivan García Topete Aug 28 '18 at 18:41