web-dev-qa-db-ja.com

.NET 4.5でこれらのメソッドの両方を「同時に」実行するにはどうすればよいですか?

私は2 独立ロジックの断片を行うメソッドを持っています。 同時に ..の両方を実行して、両方の子メソッドが完了した後にのみ続行できることを望んでいました。

_async/await_構文に頭を悩ませようとしましたが、理解できませんでした。

コードは次のとおりです。

_public PewPew SomeMethod(Foo foo)
{
    var cats = GetAllTheCats(foo);
    var food = GetAllTheFood(foo);

    return new PewPew
               {
                   Cats = cats,
                   Food = food
               };
}

private IList<Cat> GetAllTheCats(Foo foo)
{
    // Do stuff, like hit the Db, spin around, dance, jump, etc...
    // It all takes some time.
    return cats;
}

private IList<Food> GetAllTheFood(Foo foo)
{
    // Do more stuff, like hit the Db, nom nom noms...
    // It all takes some time.
    return food;
}
_

したがって、上記のコードでは、すべての猫と餌を同時に手に入れてください。終了したら、新しいPewPewを返します。

上記のどのクラスがasyncなのか、Taskなどを返すのかわからないため、混乱しています。プライベートな2つだけですか?私はTask.WaitAll(tasks)メソッドを活用する必要があると推測していますが、同時に実行するタスクをsetupする方法がわかりません。

提案、親切な人?

39
Pure.Krome

あなたがしたいことがあるかもしれません:

_public async Task<PewPew> SomeMethod(Foo foo)
{
    // get the stuff on another thread 
    var cTask = Task.Run(() => GetAllTheCats(foo));
    var fTask = Task.Run(() => GetAllTheFood(foo));

    var cats = await cTask;
    var food = await fTask;

    return new PewPew
               {
                   Cats = cats,
                   Food = food
               };
}

public IList<Cat> GetAllTheCats(Foo foo)
{
    // Do stuff, like hit the Db, spin around, dance, jump, etc...
    // It all takes some time.
    return cats;
}

public IList<Food> GetAllTheFood(Foo foo)
{
    // Do more stuff, like hit the Db, nom nom noms...
    // It all takes some time.
    return food;
}
_

ここで理解する必要がある2つのことがあります。

1)この違いは何ですか:

_var cats = await cTask;
var food = await fTask;
_

この:

_Task.WaitAll(new [] {cTask, fTask});
_

どちらも2つのasyncタスクを終了させて​​から_return new PewPew_をさせるという意味で同様の結果をもたらします-ただし、違いはTask.WaitAll()が現在のスレッドをブロックすることです(UIの場合スレッド、UIがフリーズします)。代わりに、awaitは、ステートマシンでSomeMethod sayを分解し、SomeMethodキーワードに遭遇すると、awaitから呼び出し元に戻ります。スレッドをブロックしません。以下のコードawaitは、asyncタスクが終了したときに実行されるようにスケジュールされます。

2)これもできます:

_var cats = await Task.Run(() => GetAllTheCats(foo));
var food = await Task.Run(() => GetAllTheFood(foo));
_

ただし、これはasyncタスクを同時に開始しません。最初のタスクが終了すると、2番目のタスクが開始されます。これは、awaitキーワードがどのように機能するかということです。

編集:SomeMethodの使用方法-呼び出しツリーの先頭のどこかで、Wait()またはResultプロパティを使用する必要があります-OR =-_async void_からawaitにする必要があります。通常、_async void_はイベントハンドラーになります。

_public async void OnSomeEvent(object sender, EventArgs ez) 
{ 
  Foo f = GetFoo();
  PewPew p = await SomeMethod(f);
}
_

そうでない場合は、Resultプロパティを使用します。

_public Foo2 NonAsyncNonVoidMethod() 
{
   Foo f = GetFoo();
   PewPew p = SomeMethod(f).Result; //But be aware that Result will block thread

   return GetFoo2(p);
}
_
51
YK1

これを行う最も簡単な方法は、 Parallel.Invoke() を使用することです

_IList<Cat> cats;
IList<Food> food;

Parallel.Invoke
(
    () => cats = GetAllTheCats(foo),
    () => food = GetAllTheFood(foo)
);
_

Parallel.Invoke()は、すべてのメソッドが戻るのを待ってから戻ります。

詳細はこちら: http://msdn.Microsoft.com/en-us/library/dd460705.aspx

Parallel.Invoke()はシステム内のプロセッサの数に応じてスケーリングを処理しますが、それは実際に重要なのは、2つ以上のタスクを開始する場合のみです。

20
Matthew Watson

非同期メソッドを使用していない場合、または.Netフレームワークの古いバージョンを使用している場合は、非同期を使用する必要はありません。Tasks 簡単にするために:

Task taskA = Task.Factory.StartNew(() => GetAllTheCats(foo));
Task taskB = Task.Factory.StartNew(() => GetAllTheFood(foo));

Task.WaitAll(new [] { taskA, taskB });
// Will continue after both tasks completed
10
Adam Tal

[〜#〜] tpl [〜#〜] を使用して、実行中に複数のタスクを待つことができます。 here を参照してください。

このような:

public PewPew SomeMethod(Foo foo) {
    IList<Cat> cats = null;
    IList<Food> foods = null;

    Task[] tasks = new tasks[2] {
        Task.Factory.StartNew(() => { cats = GetAllTheCats(foo); }),
        Task.Factory.StartNew(() => { food = GetAllTheFood(foo); })
    };

    Task.WaitAll(tasks);

    return new PewPew
               {
                   Cats = cats,
                   Food = food
               };
}
0
Maarten

他の答えに追加すると、次のようなことができます:

public PewPew SomeMethod(Foo foo)
{
    Task<IList<Cat>> catsTask = GetAllTheCatsAsync(foo);
    Task<IList<Food>> foodTask = GetAllTheFoodAsync(foo);

    // wait for both tasks to complete
    Task.WaitAll(catsTask, foodTask);

    return new PewPew
    {
        Cats = catsTask.Result,
        Food = foodTask.Result
    };
}

public async Task<IList<Cat>> GetAllTheCatsAsync(Foo foo)
{
    await Task.Delay(7000); // wait for a while
    return new List<Cat>();
}

public async Task<IList<Food>> GetAllTheFoodAsync(Foo foo)
{
    await Task.Delay(5000); // wait for a while
    return new List<Food>();
}
0