简体   繁体   中英

Understanding Delegates and Task

public static Task<TResult> ForEachParallel<TItem, TSubResult, TResult, TParam>(this IEnumerable items, Func<TItem, TParam, TSubResult> map, Func<TSubResult[], TResult> reduce, TParam param)
{
    if (items == null) { throw new ArgumentNullException("items"); }
    if (map == null) { throw new ArgumentNullException("map"); }
    if (reduce == null) { throw new ArgumentNullException("reduce"); }

    return Task<TResult>.Factory.StartNew(() =>
    {
        List<Task<TSubResult>> tasks = new List<Task<TSubResult>>();

        foreach (TItem item in items)
        {
            Task<TSubResult> t = Task<TSubResult>.Factory.StartNew(item2 =>
            {
                var mparam = (Tuple<TItem, TParam>)item2;
                return map(mparam.Item1, mparam.Item2);
            },
                new Tuple<TItem, TParam>(item, param),
                TaskCreationOptions.None | TaskCreationOptions.AttachedToParent);
            tasks.Add(t);
        }

        List<TSubResult> results = new List<TSubResult>();
        foreach (Task<TSubResult> task in tasks)
        {
            results.Add(task.Result);
        }

        return reduce(results.ToArray());
    });
}

static void Main(string[] args)
{
    var a = Generate().ForEachParallel<int, int, int, int>(
        (element, param) =>
        {
            var result = element * param;
            Console.WriteLine("Map: {0}, {1}", result, Task.CurrentId);
            Thread.Sleep(new Random(DateTime.Now.Millisecond).Next(500));
            return result;
        },
        (subresult) =>
        {
            var sum = 0;
            foreach (var item in subresult)
            {
                        sum += item;
                    }
                    Console.WriteLine("Reduce: {0}", sum);
                    return sum;
                },
                5);

            Console.WriteLine(a.Result);
        }
}

static IEnumerable Generate()
{
    for (int i = 0; i < 100; i++)
    {
        yield return i;
    }
}

I have a few questions regarding the program:

  1. In the main() , why is it that the author is not populating 4 values (viz, element, param,subresult)?
  2. In the main() , what are element and param values?
  3. In the ForEachParallel() , what is the role of new Tuple<TItem, TParam>(item, param) ?
  4. In the ForEachParallel() , var mparam = (Tuple<TItem, TParam>)item2 , what is item2? Is the author calling the Tuple function?

It's easiest to explain this with a simple example.

This code:

private static string UppercaseString(string inputString)
{
    return inputString.ToUpper();
}

public static void Main()
{
    var result = UppercaseString("hello");
}

Does exactly the same as:

private static string UppercaseString(string inputString)
{
    return inputString.ToUpper();
}

public static void Main()
{
    Func<string, string> convert = UppercaseString;
    var result = convert("hello");
}

And exactly the same as:

public static void Main()
{
    Func<string, string> convert = inputString => inputString.ToUpper();
    var result = convert("hello");
}

The first example is the traditional way of doing things.

The second example uses a Func<string, string> to create a pointer to the method (a delegate).

The third example uses a lambda expression.

In all cases inputString is the parameter of the method. The value of inputString is set to "hello" when calling the method.

The same goes for element , param , subresult and item2 in your example code. They are all parameters of the delegates. Whatever is calling these delegates is responsible for putting in arguments.

What might make your code a little harder to understand is that the delegates are used as parameters in other methods. For example:

private static string Hello(Func<string, string> func)
{
    return func("hello");
}

public static void Main()
{
    Func<string, string> convert = inputString => inputString.ToUpper();
    var result = Hello(convert);
}

See also the MSDN docs on this subject: https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx

Before delving into how this piece of code works there is some C# language theory which is very useful to know. I'll indicate a link near each answer.

a).b). The elements which you are expecting to be populated will have values when the lambda expression will be called. What you see in the main are lambda expressions which are not called when they are declared but when they are used. First lambda will be called on the line return map(mparam.Item1, mparam.Item2); Second lambda will be called on the line return reduce(results.ToArray()); A place to start for this lambda expressions can be here .

c). When StartNew method is called you have the option to call it with different set of methods ( method overloading ). So here the call is made to this particular method signature:

public Task StartNew(Action<Object> action,
                     Object state,
                     TaskCreationOptions creationOptions)

Which means that the second parameter of this method (state) will be the input parameter for the previous action.

d). So the newly created Tuple object will be passed to the lambda expression:

item2 =>
{
    var mparam = (Tuple<TItem, TParam>)item2;
    return map(mparam.Item1, mparam.Item2);
}, 

Basically the Action<object> delegate represents a method which accepts a parameter of type Object as input but you need a Tuple to access its items (Item1 and Item2). This line var mparam = (Tuple<TItem, TParam>)item2 makes an explicit conversion from object to Tuple.

To find out why the 4th parameter is not passed you need to know about Method Extensions . In fact the first parameter is not passed because the method is called by using an object of type IEnumerable. This method extends the class IEnumerable without touching the original class so the way to say 'I want this method to extend IEnumerable and use it for every IEnumerable object' is by setting first parameter to IEnumerable. The compiler will make sure that it sends the list as first parameter so in the extension method you have it for use.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM