Reputation: 11
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
Upvotes: 1
Views: 129
Reputation: 8753
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.
Upvotes: 1
Reputation: 156728
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.
Upvotes: 3