1

I have a scenario where I would like to invoke functions but want them to be invoke conditionally. So in the below code only function 2 and 3 will be invoked. However, the Action part doesnt return a value but in my case I want to store the return value.

List<int> list = new List<int> {2,3};
Dictionary<int, Action> actions = new Dictionary<int, Action>()
{
   {1, Function1},
   {2, Function2},
   {3, Function3}
};

Parallel.Invoke((from action in list select actions[action]).ToArray());

Initially what I had was below code but this would invoke all the functions. Any thoughts?

Parallel.Invoke(
 () => return1=function1,
 () => return2=function2,
 () => return3=function3
);
Kar
  • 105
  • 11

3 Answers3

1

If you need results from the execution, Parallel and Action won't be able to get you the function results, but if we use Task and Func<T> we can get the results back after running them in parallel.

I added to your example below to use Task instead of Parallel to have the functions run concurrently AND allow you to store the results. I assumed int for the return type of functions 1, 2, and 3 - you can change it to fit your needs.

List<int> list = new List<int> { 2, 3 };
Dictionary<int, Func<int>> actions = new Dictionary<int, Func<int>>()
{
    {1, Function1},
    {2, Function2},
    {3, Function3}
};


List<Task<int>> taskList = (from a in list select Task.Run(actions[a])).ToList();

// Allow all processing to finish before accessing results
Task.WaitAll(taskList.ToArray());
int result = taskList[0].Result;

Final note, you may be able to swap taskList.ForEach(...) with Parallel.ForEach(...), but I think that will introduce some unnecessary overhead.

Mister P
  • 46
  • 4
  • Why are you creating the `Task`s using the constructor and then calling `Start`, instead of just using `Task.Run`? – svick Apr 22 '17 at 11:48
  • Good point, I was messing around with using `Parallel.ForEach` but didn't switch back to `Task` correctly. The best solution is to use `Task.Run` instead of `Task.Start`. – Mister P Apr 24 '17 at 22:17
1

If I learned it correctly, you just want to execute some actions that are present in list. Why not just write it at C#?

var hs = new HashSet<int> { 2, 3 };
var actions = new Dictionary<int, Action>()
{
   {1, Function1},
   {2, Function2},
   {3, Function3}
};

actions.Where(x => hs.Contains(x.Key)).AsParallel().ForAll(x => x.Value());

How they read it: take all pairs with key presented in set of required keys (2 and 3 in this example), and then execute theirs actions in parallel manner. Pretty clear, I guess.

If you want return some values, use Func and Select instead of Action and ForAll respectively.

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
0

I am dealing with some parallel tasks right now, and I was using Parallel.Invoke, I ended up switching the way I was calling the actions to be in a list, and called the Parallel.ForEach on it. Which ended up shaving off 1.2 ms/per row for me. Which was awesome. So I figured I would take a stab at what I think you were asking.

        int Function1(int input)
        {
            return 10 * input;
        }
        int Function2(int input)
        {
            return 20 * input;
        }
        int Function3(int input)
        {
            return 30 * input;
        }
        List<int> list = new List<int> { 2, 3 };
        Dictionary<int, Func<int, int>> actions = new Dictionary<int, Func<int, int>>()
        {
           {1, (arg) => Function1(arg)},
           {2, (arg) => Function2(arg)},
           {3, (arg) => Function3(arg)}
        };
        var bag = new ConcurrentBag<int>();

        Parallel.ForEach(actions,  action => { 
            if(action.Key == 2 || action.Key == 3)
            {
                bag.Add(action.Value.Invoke(3));
            }                
        });

        foreach(var number in bag)
        {
            Console.WriteLine(number);
        }

I think what you were trying to do is conditionally call the methods, and then store off the return from those Funcs. I was a little confused because you were using Action (which return nothing). So I switched them to Func which is Function takes an int, and returns and int. I also added some inline functions just so the code would be able to run from a copy and paste.

Also here is a .net fiddle with the solution if you want to play around with it a bit.

DeadlyChambers
  • 5,217
  • 4
  • 44
  • 61