繁体   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;
    }
}

我对程序有一些疑问:

  1. main() ,为什么作者未填充4个值(即,元素,参数,子结果)?
  2. main() ,元素和参数值是什么?
  3. ForEachParallel()new Tuple<TItem, TParam>(item, param)什么?
  4. ForEachParallel()var mparam = (Tuple<TItem, TParam>)item2 ,item2是什么? 作者在调用元组函数吗?

用一个简单的例子来解释这是最容易的。

这段代码:

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

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

完全相同:

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

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

与以下内容完全相同:

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

第一个例子是传统的做事方式。

第二个示例使用Func<string, string>创建指向该方法的指针(委托)。

第三个示例使用lambda表达式。

在所有情况下, inputString是方法的参数。 调用该方法时, inputString的值设置为"hello"

示例代码中的elementparamsubresultitem2也是一样。 它们都是代表的参数。 无论调用什么这些代表,都要负责进行辩论。

使您的代码更难理解的是,委托在其他方法中用作参数。 例如:

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);
}

另请参阅有关此主题的MSDN文档: https : //msdn.microsoft.com/zh-cn/library/bb549151(v=vs.110).aspx

在研究这段代码的工作方式之前,有一些C#语言理论非常有用。 我将在每个答案附近指出一个链接。

a).b)。 当调用lambda表达式时,期望填充的元素将具有值。 您在主体中看到的是lambda表达式,它们在声明时不被调用,而在使用时被调用。 第一个lambda将在行return map(mparam.Item1, mparam.Item2);上调用return map(mparam.Item1, mparam.Item2); 第二个lambda将在return reduce(results.ToArray());的行上调用return reduce(results.ToArray()); 这个lambda表达式的起始位置可以在这里

C)。 调用StartNew方法时,可以选择使用不同的方法集来调用它( 方法重载 )。 因此,在此调用此特定方法签名:

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

这意味着此方法的第二个参数(状态)将是前一个动作的输入参数。

d)。 因此,新创建的Tuple对象将传递给lambda表达式:

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

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

要找出为什么不传递第4个参数,您需要了解方法扩展 实际上,没有传递第一个参数,因为使用类型为IEnumerable的对象调用了该方法。 此方法扩展类IEnumerable而不接触原始类,因此说“我希望此方法扩展IEnumerable并将其用于每个IEnumerable对象”的方式是通过将第一个参数设置为IEnumerable。 编译器将确保将列表作为第一个参数发送,因此在扩展方法中您可以使用它。

暂无
暂无

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

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