4

クラス構造を考えると:

public class Foo
{
    public IEnumerable<Bar> GetBars()
    {
        for(int i = 0; i < 1000; i++)
        {
            Thread.Sleep(1000);
            yield return new Bar() { Name = i.ToString() };
        }
    }
}

public class Bar
{
    public string Name { get; set; }
}

のリストがあり、メソッド内でIEnumerable<Foo>次を取得する時間は非常に遅いです (上記の でシミュレート)。BarGetBars()Thread.Sleep(1000)

私は次のことをしたいと思います:

myFoo.AsParallel().SelectMany(foo => foo.GetBars().Select(bar => bar.Name))

Barしかし、遅延があるため、それぞれの次の値をプリロードし続けてから、アクセス可能になる順序でfor each を互いにFooマージしたいと考えています。IEnumable<Bar>Foo

Tpl Dataflow async nuget libary (具体的にはTransformBlockand 程度は低いActionBlock) を調べてきましたが、私がやろうとしていることを実行するのに役立つものは何も見つかりません。

4

4 に答える 4

2

Bar問題は、並列であろうとなかろうと、最初のオブジェクトを取得するまで、2 番目のオブジェクトの取得を開始することさえできないことです。PLINQ の使用は、LINQ 機能を介して各オブジェクトで長時間実行される処理を実行している場合にのみ役立ちます。遅延が基になるIEnumerable.

Task1 つのオプションは、反復子の移動にほとんど時間がかからないように、一連のオブジェクトを返すことです。

public async Task<Bar> GenerateFoo()
{
    await Task.Delay(1000);
    return new Bar() { Name = i.ToString() };
}

public IEnumerable<Task<Bar>> GetBars()
{
    for(int i = 0; i < 1000; i++)
    {
        yield return GenerateFoo();
    }
}

そのコードを使用すると、イテレータを移動するだけで、完了するまで待つのではなく、 の生成が開始されます。Barそれができたら、各タスクに継続を追加して各 の処理を​​処理するか、またはBarなどのメソッドを使用してそれらがすべて完了するのを待つことができます。Task.WaitAllTask.WhenAll

于 2013-05-28T14:16:46.013 に答える
1

IEnumerable<T>私は、prefectching を実行する新しい実装を作成することになりました。

public IEnumerator<T> GetEnumerator()
{
    TaskFactory<T> taskFactory = new TaskFactory<T>();
    Task<T> task = null;
    IEnumerator<T> enumerator = Source.GetEnumerator();

    T result = null;
    do
    {
        if (task != null)
        {
            result = task.Result;
            if (result == null)
                break;
        }

        task = taskFactory.StartNew(() =>
        {
            if (enumerator.MoveNext())
                return enumerator.Current;
            else
                return null;
        });
        if (result != null)
            yield return result;
    }
    while (task != null);
}

最初の結果を返す前に最初の 2 つの結果を要求するだけで、生成された結果の前に常に 1 つの結果要求を保持します。

于 2013-05-30T10:40:34.523 に答える
1

以下のように拡張メソッドを記述して、バーが利用可能になるとすぐに (任意の列挙型で) バーを生成できます。

myFoo.Select(x=>x.GetBars()).Flatten().Select(bar => bar.Name)

public static class ParallelExtensions
{
    public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> enumOfEnums)
    {
        BlockingCollection<T> queue = new BlockingCollection<T>();

        Task.Factory.StartNew(() =>
        {
            Parallel.ForEach(enumOfEnums, e =>
            {
                foreach (var x in e)
                {
                    queue.Add(x);
                }
            });
            queue.CompleteAdding();
        });

        return queue.GetConsumingEnumerable();
    }
}
于 2013-05-28T14:36:22.547 に答える