0

C#のTPLでいくつかのテストを行っています。C#のTPLを使用したasync/await/resultの不一致

以下のテストでは、最初のものは、async/awaitを使用せず、期待どおりの動作をし、常に期待した値を返します。しかし、async/awaitを使った最後の2つのテストは非常に矛盾しています。私のテストで

、私は次のような仮定をした:

  1. GetThreadIdInstantが原因タスクであることcompletetdに同じスレッドに返します。
  2. GetThreadIdDelayedは、遅延が即座に戻ってこないため、別のスレッドで返されます。
  3. GetThreadIdForcedNewは、Task.Run()の使用のために別のスレッドで返されます。

async/awaitを使用している場合があるタスクに.Resultを使用する時には、上記の仮定が真である理由の説明が、一貫して真実ではないですか? を使用している最後の2つのテストでは、.Resultを使用した最初のテストと同じ結果を返すことを期待しましたが、これは真ではありません。しかし、私がforループの中にコードを持っている理由は、いくつかの反復が機能し、後で私のAssertステートメントが失敗するということです。私が "矛盾"という言葉を使用した理由は、テストを継続的に実行しているb/cであり、デバッグとデバッグを交互に実行すると、時には合格しないことがあります。

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Parallels.Tests 
{ 
    [TestClass] 
    public class GetterTests 
    { 
     //this test always succeeds 
     [TestMethod] 
     public void ResultTest() 
     { 
      for (var i = 0; i < 500; i++) 
      { 
       var currentThreadId = Thread.CurrentThread.ManagedThreadId; 

       var instantThreadId = ThreadGetter.GetThreadIdInstant().Result; 
       var delayedThreadId = ThreadGetter.GetThreadIdDelayed().Result; 
       var forcedNewThreadId = ThreadGetter.GetThreadIdForcedNew().Result; 

       Assert.AreEqual(currentThreadId, instantThreadId); 
       Assert.AreNotEqual(currentThreadId, delayedThreadId); 
       Assert.AreNotEqual(currentThreadId, forcedNewThreadId); 
      } 
     } 

     //mixed results 
     [TestMethod] 
     public async Task AwaitDelayedTest() 
     { 

      for (var i = 0; i < 500; i++) 
      { 
       try 
       { 
        var currentThreadId = Thread.CurrentThread.ManagedThreadId; 

        var delayedThreadId = await ThreadGetter.GetThreadIdDelayed(); 

        Assert.AreNotEqual(currentThreadId, delayedThreadId); 
       } 
       catch (Exception ex) 
       { 
        throw new Exception($"failed at iteration: {i}", ex); 
       } 
      } 

     } 

     //mixed results 
     [TestMethod] 
     public async Task AwaitForcedNewTest() 
     { 
      for (var i = 0; i < 500; i++) 
      { 
       try 
       { 
        var currentThreadId = Thread.CurrentThread.ManagedThreadId; 

        var forcedNewThreadId = await ThreadGetter.GetThreadIdForcedNew(); 

        Assert.AreNotEqual(currentThreadId, forcedNewThreadId); 
       } 
       catch (Exception ex) 
       { 
        throw new Exception($"failed at iteration: {i}", ex); 
       } 
      } 
     } 
    } 

    public static class ThreadGetter 
    { 
     public static async Task<int> GetThreadIdInstant() => Thread.CurrentThread.ManagedThreadId; 

     public static async Task<int> GetThreadIdDelayed() 
     { 
      await Task.Delay(1); 
      return Thread.CurrentThread.ManagedThreadId; 
     } 

     public static async Task<int> GetThreadIdForcedNew() => await Task.Run(() => Thread.CurrentThread.ManagedThreadId); 
    } 
} 
+2

「非常に矛盾する」という意味を正確に説明すると、本当に役に立ちます。それらの結果が何であるかを知らなくても結果を説明するのは難しいです。 –

+1

また、[TPL](https://msdn.microsoft.com/en-us/library/hh873175(v=vs.110).aspx)ではなく、[TPL](https://msdn.microsoft .com/ja-us/library/dd460717(v = vs.110).aspx)。あなたの質問にはTPL特有のものはありません。 – sellotape

+0

私は「矛盾している」という意味をもっと明確にしようとしました。 – Sharpiro

答えて

2

this answerを関連する質問にしてください。

あなたが.Resultを使用する場合)...あなたは現在のスレッドをブロックしているので、(あなたのGetThreadIdDelayedのタスクで行われた作業とGetThreadIdForcedNew()メソッド呼び出し側のスレッド以外のスレッドで常に実行されます。

awaitを使用すると...呼び出しスレッドで実行が継続されないにもかかわらず、そのスレッドはサブタスクの実行中に消費されず、一部のサブタスク自体の処理に使用されることがあります。

また、awaitを呼び出した後の実行は、デフォルトでは常に同じスレッド上に戻るとは限りません。これは、たとえば、多くのUIアプリケーションでは特に重要です。 ConfigureAwaitを使用してこの動作を制御できます。

関連する問題