다른 작업을 기다리는 시작되지 않은 작업을 선언하는 방법은 무엇입니까?


9

이 단위 테스트를 수행했는데 "await Task.Delay ()"가 대기하지 않는 이유를 이해하지 못합니다!

   [TestMethod]
    public async Task SimpleTest()
    {
        bool isOK = false;
        Task myTask = new Task(async () =>
        {
            Console.WriteLine("Task.BeforeDelay");
            await Task.Delay(1000);
            Console.WriteLine("Task.AfterDelay");
            isOK = true;
            Console.WriteLine("Task.Ended");
        });
        Console.WriteLine("Main.BeforeStart");
        myTask.Start();
        Console.WriteLine("Main.AfterStart");
        await myTask;
        Console.WriteLine("Main.AfterAwait");
        Assert.IsTrue(isOK, "OK");
    }

다음은 단위 테스트 출력입니다.

단위 테스트 출력

이것이 어떻게 "대기"가 기다리지 않고 주 스레드가 계속됩니까?


당신이 달성하려고하는 것이 약간 불분명합니다. 예상 출력을 추가 할 수 있습니까?
OlegI

1
테스트 방법은 매우 명확합니다 – isOK가 맞을 것으로 예상됩니다
Sir Rufo

시작되지 않은 작업을 생성 할 이유가 없습니다. 작업은 스레드가 아니며 스레드를 사용 합니다. 무엇을하려고합니까? 왜 Task.Run()처음 사용하지 Console.WriteLine않습니까?
Panagiotis Kanavos

1
@Elo 방금 스레드 풀을 설명했습니다. 당신은 하지 않는 작업 큐를 구현하는 작업의 풀 (pool)이 필요합니다. 당신은 작업의 큐를 필요 예를 들어, 액션은 <T는> 객체
파나지오티스 Kanavos

2
@Elo는 ActionBlock과 같은 TPL Dataflow 클래스 또는 최신 System.Threading.Channels 클래스를 통해 .NET에서 이미 사용할 수 있습니다. 하나 이상의 동시 작업을 사용하여 메시지를 수신하고 처리하기 위해 ActionBlock을 작성할 수 있습니다. 모든 블록에는 구성 가능한 용량을 가진 입력 버퍼가 있습니다. DOP 및 용량은 동시성, 스로틀 요청을 제어하고 배압을 구현할 수 - 너무 많은 메시지를 대기하는 경우, 생산자 기다리고
파나지오티스 Kanavos

답변:


8

new Task(async () =>

작업은을 수행하지 않고을 수행 Func<Task>합니다 Action. 비동기 메소드를 호출하고 리턴 될 때 종료 될 것으로 예상합니다. 그러나 그렇지 않습니다. 작업을 반환합니다. 새 작업이 해당 작업을 기다리지 않습니다. 새 작업의 경우 메서드가 반환되면 작업이 완료됩니다.

새 작업으로 래핑하는 대신 이미 존재하는 작업을 사용해야합니다.

[TestMethod]
public async Task SimpleTest()
{
    bool isOK = false;

    Func<Task> asyncMethod = async () =>
    {
        Console.WriteLine("Task.BeforeDelay");
        await Task.Delay(1000);
        Console.WriteLine("Task.AfterDelay");
        isOK = true;
        Console.WriteLine("Task.Ended");
    };

    Console.WriteLine("Main.BeforeStart");
    Task myTask = asyncMethod();

    Console.WriteLine("Main.AfterStart");

    await myTask;
    Console.WriteLine("Main.AfterAwait");
    Assert.IsTrue(isOK, "OK");
}

4
Task.Run(async() => ... )또한 옵션입니다
Sir Rufo

당신은 질문의 저자와
똑 같았습니다

BTW myTask.Start();올릴 것이다InvalidOperationException
선생님 Rufo

@OlegI 보지 못합니다. 설명해 주시겠습니까?
nvoigt

@nvoigt 나는 그가 myTask.Start()그의 대안에 대한 예외를 낳을 것을 의미한다고 가정하고 사용시 제거해야합니다 Task.Run(...). 솔루션에 오류가 없습니다.
404

3

문제는 제네릭이 아닌 Task클래스를 사용하고 있다는 것입니다. 결과가 아닙니다. 따라서 Task비동기 대리자를 전달하는 인스턴스 를 만들 때 :

Task myTask = new Task(async () =>

... 대리자는로 취급됩니다 async void. 는 async void이고 Task, 기다릴 수 없으며, 예외를 처리 할 수 ​​없으며, StackOverflow 및 다른 곳에서 좌절 된 프로그래머가 만든 수천 가지 질문 의 원천입니다 . 해결책은 일반 Task<TResult>클래스 를 사용 하는 것입니다. 결과를 반환하려고하고 결과가 다른 것 Task입니다. 따라서 다음을 만들어야합니다 Task<Task>.

Task<Task> myTask = new Task<Task>(async () =>

이제 당신 Start이 외부 Task<Task>를 만들 때 그 작업은 단지 내부를 만드는 것이므로 거의 즉시 완료됩니다 Task. 그러면 내부 Task도 기다려야합니다 . 이것이 가능한 방법입니다 :

myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;

두 가지 대안이 있습니다. 내부에 대한 명시 적 참조가 필요하지 않은 경우 Task외부를 Task<Task>두 번 기다릴 수 있습니다 .

await await myTask;

... Unwrap외부 및 내부 작업을 하나로 결합하는 기본 제공 확장 방법 을 사용할 수 있습니다 .

await myTask.Unwrap();

이 언 랩핑은 인기있는 Task.Run작업을 생성 하는 훨씬 더 인기있는 방법을 사용할 때 자동으로 발생 하므로 Unwrap요즘에는 자주 사용되지 않습니다.

비동기 델리게이트가 결과 (예 : a string)를 리턴해야한다고 결정한 경우 myTask변수를 유형으로 선언해야 합니다 Task<Task<string>>.

참고 :Task 차가운 작업을 생성하기 위해 생성자를 사용한다고 보증하지 않습니다 . 실습은 일반적으로 찌그러짐에 따라 실제로 모릅니다. 그러나 아마도 거의 사용되지 않기 때문에 코드의 다른 알지 못하는 사용자 / 유지 보수 자 / 검토자를 잡을 가능성이 있습니다.

일반적인 조언 : 비동기 델리게이트를 메소드의 인수로 제공 할 때마다주의하십시오. 이 메소드는 이상적으로 Func<Task>인수 (비동기 대리자를 이해함) 또는 적어도 Func<T>인수 (적어도 생성 된 Task것은 무시되지 않음)를 예상해야합니다. 불행히도이 방법 Action이을 (를) 수락하면 대리인이로 처리됩니다 async void. 이것이 당신이 원하는 경우는 거의 없습니다.


자세한 기술 답변! 감사합니다.
Elo

@ 엘로 내 기쁨!
Theodor Zoulias

1
 [Fact]
        public async Task SimpleTest()
        {
            bool isOK = false;
            Task myTask = new Task(() =>
            {
                Console.WriteLine("Task.BeforeDelay");
                Task.Delay(3000).Wait();
                Console.WriteLine("Task.AfterDelay");
                isOK = true;
                Console.WriteLine("Task.Ended");
            });
            Console.WriteLine("Main.BeforeStart");
            myTask.Start();
            Console.WriteLine("Main.AfterStart");
            await myTask;
            Console.WriteLine("Main.AfterAwait");
            Assert.True(isOK, "OK");
        }

여기에 이미지 설명을 입력하십시오


3
당신은없이 실현 await, 작업 지연이 실제로 바로 그 작업을 지연하지 않습니다? 기능을 제거했습니다.
nvoigt

방금 테스트 한 결과 @nvoigt가 맞습니다 : 경과 시간 : 0 : 00 : 00,0106554. 그리고 우리는 당신의 스크린 샷에서 볼 수 있습니다 : "경과 시간 : 18 ms",> = 1000 ms
Elo

네 당신 말이 맞아요, 나는 나의 응답을 업데이트합니다. Tnx. 당신이 코멘트를 한 후에 나는 최소한의 변화로 이것을 해결합니다. :)
BASKA

1
wait ()를 사용하고 Task 내부에서 기다리지 않는 것이 좋습니다! 감사 !
Elo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.