1

I have a list actions calling different methods. All actions will need to run in different threads in parallel using Parallel.ForEach. All actions will return a bunch of info as an object Result. All Results are added to a log using a lock on the log.

My question and possible issue is: can the action methods have a return value to be retrieved and used inside the Parallel.ForEach and how would that be done? Also, how could the Result be safely used without being overwritten every time a thread finishes execution while another result is waiting for the log to be updated?

My current simplified code looks something like this:

    private Log _log;

    private void DoParallelWork()
    {
        List<Action> actions = new List<Action> { Action1, Action2, Action3, ...etc };
        Result result = new Result();

        System.Threading.Tasks.Parallel.ForEach(actions , action =>
        {
            **result** = action(); // How can I get the result back to the ForEach?

            lock (_log)
            {
                UpdateLog(result);
            }
        });
    }
   
    private Result Action1() // same structure for Action2, 3, ...etc
    {
        return [call to the db to execute a stored procedure returning a Result object];
    }

    private void UpdateLog(Result result)
    {
        // update _log based on result
    }

Thanks for helping out

3
  • 1
    Do not use Action type, because Action has no returns value. You should set list as new List<Func<Result>> Commented Jul 6, 2023 at 21:43
  • ​Related: How do I collect return values from Parallel.ForEach? Commented Jul 6, 2023 at 23:07
  • 1
    @PsiHamster that is exactly what I was looking for. The rest of the code I have adapted according to StriplingWarrior's reply for the advantage of the ToList() gathering all the results after all threads are done safely. Thank you :) Commented Jul 7, 2023 at 6:02

2 Answers 2

3

Typically if you want to return values from parallel operations like this, it's better to use PLINQ.

var results = actions
    .AsParallel()
    .Select(action => action())
    .ToList();

Of course, you can adapt the lambda to add in your locking and logging as necessary. Just return the result of the action, if you change it to a block.

Sign up to request clarification or add additional context in comments.

3 Comments

The nice thing is that when you do it this way, no locking should be necessary, since the ToList call assembles the results after all the threads have finished.
Thank you. This is brilliant as it solves the integrity part of the results. Did have one issue running this code, as it wouldn't let me retrieve any results in the first place. But have used @PsiHamster's suggestion and replaced the list of actions with a List<Func<Result>> and it works perfectly fine.
@JohnWu On the other hand, by this approach you won't get any live updates during the execution of the actions, but only when everything is finished.
1

Action is for void methods. If you use Action, the compiler assumes that the methods don't have any return type and that is why you can't use it the way you intended.

If you have a return type, you have to use the Func delegate types. In your case, it is Func<Result>, because your methods return a Result and don't have any input parameters. This will be your code:

private void DoParallelWork()
{
    List<Func<Result>> actions = new List<Func<Result>> { Action1, Action2, Action3, ...etc };

    System.Threading.Tasks.Parallel.ForEach(actions , action =>
    {
        Result result = action();
        lock (_log)
        {
            UpdateLog(result);
        }
    });
}

Note that I put the declaration of the result variable inside the parallel foreach, because otherwise the threads would share this variable. This might have the consequence that the result of one of the methods is overwritten with the result from another method. For thread-safety, it is important that you share as few variables as possible.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.