【问题标题】:Loosely-ordered concurrency with loops?带有循环的松散顺序并发?
【发布时间】:2014-11-29 23:11:48
【问题描述】:

我有以下代码用于从 REST 服务(Get(i) 调用)中获取数据,然后用它们的关系填充一个矩阵(未显示;这发生在 addLabels() 中)。

所有Get() 调用都可以彼此并行运行,但它们都必须在任何内容进入第二个循环之前完成(再次,调用可以彼此并行运行)。 addLabel() 调用依赖于 Get() 调用中的工作才能完成。

** 对于任何偶然发现这篇文章的人,这段代码就是解决方案:**

private async void GetTypeButton_Click(object sender, RoutedEventArgs e)
{
    await PokeType.InitTypes(); // initializes relationships in the matrix

    var table = PokeType.EffectivenessMatrix;

    // pretty-printing the table
    // ...
    // ...
}

private static bool initialized = false;

public static async Task InitTypes()
{
    if (initialized) return;

    // await blocks until first batch is finished
    await Task.WhenAll(Enumerable.Range(1, NUM_TYPES /* inclusive */).Select(i => Get(i)));

    // doesn't need to be parallelized because it's quick work.
    foreach(PokeType type in cachedTypes.Values)
    {
        JObject data = type.GetJsonFromCache();
        addLabels(type, (JArray)data["super_effective"], Effectiveness.SuperEffectiveAgainst);
        addLabels(type, (JArray)data["ineffective"], Effectiveness.NotVeryEffectiveAgainst);
        addLabels(type, (JArray)data["no_effect"], Effectiveness.UselessAgainst);
    }

    initialized = true;
}

public static async Task<PokeType> Get(int id);

由于当前编写的代码,InitTypes() 方法尝试同时进入两个循环; cachedTypes 字典是空的,因为第一个循环还没有完成填充它,所以它永远不会运行,也没有构造任何关系。

我怎样才能正确地构造这个函数?谢谢!

【问题讨论】:

  • 您不应该用答案更新您的问题,而是发布问题并创建一个 编辑 标题以指明解决方案,以便其他人可以看到问题所在是以及如何解决的。

标签: c# .net concurrency windows-runtime async-await


【解决方案1】:

Parallel 和 async-await 不能很好地结合在一起。您的异步 lambda 表达式实际上是 async void,因为 Parallel.For 排除了 Action&lt;int&gt;,这意味着 Parallel.For 不能等待该操作完成。

如果您尝试同时多次调用Get(i) 并等待它们完成后再继续,您需要使用Task.WhenAll

await Task.WhenAll(Enumerable.Range(1, NUM_TYPES).Select(() => Get(i)))

【讨论】:

  • 此外,Parallel.ForEach() 循环是否应该首先并行存在争议。 addLabels() 可能是线程安全的,也可能不是线程安全的,但即使是,那里的计算真的足够慢,足以证明使用 Parallel 而不是普通的 foreach 语句的开销是合理的吗?
  • @i3arnon 请原谅以前没有使用过这个的人;我看到它是如何代替标准 for 循环的,但是我如何(干净地)使用它来替换 for-each?
  • @PeterDuniho 没有意识到 Parallel.ForEach 有很多开销。他们只是在做 JSON 解析,所以我可能可以同步做这些。一个标准的 foreach 就可以了,因为前一个循环正在等待,对吧?
  • @Benjin 是的。由于它是同步的,如果你想并行化它,你可以使用ParallelTask.Run(只要你确保它是线程安全的)。
猜你喜欢
  • 1970-01-01
  • 2019-06-11
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-06
相关资源
最近更新 更多