简体   繁体   English

了解代表和任务

[英]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)? main() ,为什么作者未填充4个值(即,元素,参数,子结果)?
  2. In the main() , what are element and param values? main() ,元素和参数值是什么?
  3. In the ForEachParallel() , what is the role of new Tuple<TItem, TParam>(item, param) ? ForEachParallel()new Tuple<TItem, TParam>(item, param)什么?
  4. In the ForEachParallel() , var mparam = (Tuple<TItem, TParam>)item2 , what is item2? ForEachParallel()var mparam = (Tuple<TItem, TParam>)item2 ,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). 第二个示例使用Func<string, string>创建指向该方法的指针(委托)。

The third example uses a lambda expression. 第三个示例使用lambda表达式。

In all cases inputString is the parameter of the method. 在所有情况下, inputString是方法的参数。 The value of inputString is set to "hello" when calling the method. 调用该方法时, inputString的值设置为"hello"

The same goes for element , param , subresult and item2 in your example code. 示例代码中的elementparamsubresultitem2也是一样。 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 另请参阅有关此主题的MSDN文档: https : //msdn.microsoft.com/zh-cn/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. 在研究这段代码的工作方式之前,有一些C#语言理论非常有用。 I'll indicate a link near each answer. 我将在每个答案附近指出一个链接。

a).b). a).b)。 The elements which you are expecting to be populated will have values when the lambda expression will be called. 当调用lambda表达式时,期望填充的元素将具有值。 What you see in the main are lambda expressions which are not called when they are declared but when they are used. 您在主体中看到的是lambda表达式,它们在声明时不被调用,而在使用时被调用。 First lambda will be called on the line return map(mparam.Item1, mparam.Item2); 第一个lambda将在行return map(mparam.Item1, mparam.Item2);上调用return map(mparam.Item1, mparam.Item2); Second lambda will be called on the line return reduce(results.ToArray()); 第二个lambda将在return reduce(results.ToArray());的行上调用return reduce(results.ToArray()); A place to start for this lambda expressions can be here . 这个lambda表达式的起始位置可以在这里

c). C)。 When StartNew method is called you have the option to call it with different set of methods ( method overloading ). 调用StartNew方法时,可以选择使用不同的方法集来调用它( 方法重载 )。 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). d)。 So the newly created Tuple object will be passed to the lambda expression: 因此,新创建的Tuple对象将传递给lambda表达式:

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). 基本上, Action<object>委托表示一种方法,该方法接受Object类型的参数作为输入,但是您需要一个Tuple来访问其项目(Item1和Item2)。 This line var mparam = (Tuple<TItem, TParam>)item2 makes an explicit conversion from object to Tuple. 这行var mparam = (Tuple<TItem, TParam>)item2进行了从对象到元组的显式转换。

To find out why the 4th parameter is not passed you need to know about Method Extensions . 要找出为什么不传递第4个参数,您需要了解方法扩展 In fact the first parameter is not passed because the method is called by using an object of type IEnumerable. 实际上,没有传递第一个参数,因为使用类型为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. 此方法扩展类IEnumerable而不接触原始类,因此说“我希望此方法扩展IEnumerable并将其用于每个IEnumerable对象”的方式是通过将第一个参数设置为IEnumerable。 The compiler will make sure that it sends the list as first parameter so in the extension method you have it for use. 编译器将确保将列表作为第一个参数发送,因此在扩展方法中您可以使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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