0

C#에서 TPL을 사용하여 몇 가지 테스트를 해본 결과 스레드가 생성되는 방식과 정확히 새로운 스레드가 시작될 때를 더 잘 이해할 수있었습니다.C#에서 TPL을 사용하여 async/await/result와 불일치가 발생했습니다.

아래 테스트에서 첫 번째 것은 async/await을 사용하지 않고 예상 한대로 동작하며 예상 한 값을 항상 반환합니다. 그러나 async/await을 사용한 마지막 2 개의 테스트는 매우 일치하지 않습니다. 내 테스트에서

, 나는 다음과 같은 가정을 만들어 :

  1. GetThreadIdInstant 인해 작업 인 completetd에 동일한 스레드에서 반환

    .
  2. GetThreadIdDelayed은 지연이 즉시 반환되지 않기 때문에 다른 스레드에서 반환됩니다.
  3. GetThreadIdForcedNewTask.Run()을 사용하기 때문에 다른 스레드에서 반환됩니다. async/await를 사용하는 경우

항상 true를 작업에 .Result를 사용하는 경우 위의 가정에 해당하는 이유에 대한 설명이 있는가, 그러나?

편집 : "일관성이없는"테스트의 명확화 : 따라서 async/await을 사용한 마지막 두 테스트에서 나는 여전히 .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/en-us/library/dd460717 (v = vs.110) .aspx). 귀하의 질문에 TPL에만 해당되는 내용은 없습니다. – sellotape

+0

"일관성없는"의미의 내용에 대해 더 많은 설명을하려고했습니다. – Sharpiro

답변

2

여기에서 관련 질문은 this answer을 참조하십시오.

당신이 이렇게 .Result가 ... 현재 스레드를 차단하고 사용하고

호출 스레드가 아닌 다른 스레드에서 GetThreadIdDelayed()와 GetThreadIdForcedNew() 메소드의 작업에 항상 실행을 수행 작동합니다.

await를 사용할 때 ... 호출 스레드에서 실행이 계속되지는 않지만 해당 스레드는 하위 작업 실행 중에 소비되지 않으며 일부 하위 작업 자체의 작업을 수행하는 데 (때로는) 사용될 수 있습니다 .

또한 await 호출 후 실행이 항상 같은 스레드에서 반환되는 것은 아닙니다. 이는 특히 많은 UI 응용 프로그램에서 중요합니다. ConfigureAwait을 사용하여이 동작을 제어 할 수 있습니다.