带循环的松散有序并发?

时间:2014-11-29 23:11:49

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

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

所有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字典是空的,因为第一个循环还没有完成填充它,所以它永远不会运行,也没有构建关系。

如何正确构建此功能?谢谢!

1 个答案:

答案 0 :(得分:1)

并行和async-await不能很好地融合在一起。您的异步lambda表达式实际上是async void,因为Parallel.For会感谢Action<int>,这意味着Parallel.For无法等待该操作完成。

如果您尝试多次同时致电Get(i)并等待它们完成,请继续使用Task.WhenAll

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