16

我希望这不是对 stackoverflow 的滥用;最近我在 Parallel Extensions 上看到了一些很棒的问题,这激起了我的兴趣。

我的问题:您是否使用并行扩展,如果是,如何使用?

我叫 Stephen Toub,在 Microsoft 的并行计算平台团队工作。我们是负责并行扩展的小组。我一直很想了解开发人员如何使用并行扩展(例如 Parallel.For、PLINQ、ConcurrentDictionary 等)、您的积极体验、消极体验、对未来的功能要求等等上。
如果您愿意分享此类信息,请在此处作为对此问题的回复或通过电子邮件私下给我stoub at microsoft dot com

我非常期待收到您的来信。

提前致谢!

4

5 回答 5

4

我正在使用 TPL 进行嵌套Parallel.ForEach调用。因为我从这些调用中访问字典,所以我必须使用ConcurrentDictionary. 虽然很好,但我有几个问题:

  • 里面的代表ForEach没有做太多的工作,所以我没有得到太多的并行性。系统似乎大部分时间都在加入线程。如果有办法弄清楚为什么它没有得到更好的并发性并改进它,那就太好了。

  • 内部ForEach迭代是通过ConcurrentDictionary实例进行的,如果我没有添加枚举器缓存,这将导致系统将大部分时间用于字典的枚举器。

  • 我的许多ConcurrentDictionary实例实际上都是集合,但没有ConcurrentSet,所以我不得不用ConcurrentDictionary.

  • ConcurrentDictionary不支持对象初始化语法,所以我不能说var dict = new ConcurrentDictionary<char, int> { { 'A', 65 } };这也意味着我不能将ConcurrentDictionary文字分配给类成员。

  • 在某些地方,我必须在 a 中查找一个键ConcurrentDictionary并调用一个昂贵的函数来创建一个值(如果它不存在)。如果有一个重载,那就太好了GetOrAddaddValueFactory只有当键不存在时才能计算值。这可以模拟,.AddOrUpdate(key, addValueFactory, (k, v) => v)但这会增加每次查找的额外委托调用的开销。

于 2010-12-13T07:55:07.443 回答
1

我还没有广泛使用它,但我肯定一直在关注它的用途,并在我们的代码库中寻找使用它的机会(不幸的是,我们的许多项目仍然受 .NET-2.0 约束暂且)。我自己想出的一个小宝石是一个独特的单词计数器。我认为这是我能想到的最快、最简洁的实现——如果有人能做得更好,那就太棒了:

private static readonly char[] delimiters = { ' ', '.', ',', ';', '\'', '-', ':', '!', '?', '(', ')', '<', '>', '=', '*', '/', '[', ']', '{', '}', '\\', '"', '\r', '\n' };
private static readonly Func<string, string> theWord = Word;
private static readonly Func<IGrouping<string, string>, KeyValuePair<string, int>> theNewWordCount = NewWordCount;
private static readonly Func<KeyValuePair<string, int>, int> theCount = Count;

private static void Main(string[] args)
{
    foreach (var wordCount in File.ReadAllText(args.Length > 0 ? args[0] : @"C:\DEV\CountUniqueWords\CountUniqueWords\Program.cs")
        .Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
        .AsParallel()
        .GroupBy(theWord, StringComparer.OrdinalIgnoreCase)
        .Select(theNewWordCount)
        .OrderByDescending(theCount))
    {
        Console.WriteLine(
            "Word: \""
            + wordCount.Key
            + "\" Count: "
            + wordCount.Value);
    }

    Console.ReadLine();
}

private static string Word(string word)
{
    return word;
}

private static KeyValuePair<string, int> NewWordCount(IGrouping<string, string> wordCount)
{
    return new KeyValuePair<string, int>(
        wordCount.Key,
        wordCount.Count());
}

private static int Count(KeyValuePair<string, int> wordCount)
{
    return wordCount.Value;
}
于 2010-12-13T22:44:14.243 回答
0

我正在使用存储超过 1 亿个项目的 ConcurrentDictionary。我的应用程序当时使用了大约 8 GB 的内存。ConcurrentDictionary 然后决定它想要在另一个 Add 上增长。并且它显然想要增长很多(一些内部的初步算法),因为它耗尽了内存。这是在具有 32GB 内存的 x64 上。

因此,我想要一个布尔值来阻止(并发)字典的自动重新增长/重新散列。然后我会在创建时使用一组固定的存储桶初始化字典(这与固定容量不同!)。随着时间的推移,随着桶中的项目越来越多,它会变得有点慢。但这将防止重新散列和过快且不必要地耗尽内存。

于 2012-06-26T12:41:22.020 回答
0

我们没有广泛使用它,但它确实派上了用场。

Parallel.Invoke()通过在调用中包含一些更耗时的步骤,我能够将一些运行时间较长的单元测试的运行时间减少到原来的 1/3 左右。

我也喜欢使用并行库来测试线程安全。我发现并报告了 Ninject 的几个线程问题,代码如下:

var repositoryTypes = from a in CoreAssemblies
                    from t in a.GetTypes()
                    where t.Name.EndsWith("Repository")
                    select t;
repositoryTypes.ToList().AsParallel().ForAll(
    repositoryType => _kernel.Get(repositoryType));

在我们的实际生产代码中,我们使用一些并行扩展来运行一些应该每隔几分钟运行一次的集成操作,这些操作主要包括从 Web 服务中提取数据。由于 Web 连接固有的高延迟,这特别利用了并行性,并允许我们的作业在它们应该再次触发之前全部完成运行。

于 2010-12-13T23:07:32.463 回答
0

我一直在我的项目MetaSharp中使用它。我有一个基于 MSBuild 的 DSL 编译管道,一个阶段类型是多对多阶段。M:M 阶段使用 .AsParallel.ForAll(...)。

这是片段

protected sealed override IEnumerable<IContext> Process()
{
    if (this.Input.Count() > 1)
    {
        this.Input
            .AsParallel<IContext>()
            .ForAll(this.Process);
    }
    else if (this.Input.Any())
    {
        this.Process(this.Input.Single());
    }

    return this.Input.ToArray();
}
于 2010-12-13T07:25:04.913 回答