-3

소켓에서 읽기를 반복하는 작업이 있습니다.

private async void readLoop() {
    while (!cts.IsCancelRequested()) {
        await socket.ReceiveAsync(data, ...);
        doWork(data);
    }
}

그리고 작업이 생성되는 즉시 실행할 필요가 없으므로Task생성자보다는Task.Run.

private Task readTask = new Task(readLoop, cts, LongRunning);
// when the task need to run:
readTask.Start()

이 시점에서 그것은 잘 작동합니다. 단, 작업이 끝나야 할 때, 전화 할 때readTask.Wait()또는await readTask, 예외는에서 일어났다ReceiveAsync또는doWork에 붙어 있지 않다.readTask. 즉 심지어 예외가 있었음을 의미합니다.readTask.Status~이다.RunToCompletereadTask.Exception~이다.null. 일치. 워드 프로세서, 비동기 메서드는 무효 반환하기 때문에 때문입니다. 그러나이 시도 할 때 :

private async Task readLoop() {...}

컴파일되지 않습니다.

error CS0407: 'Task WebSocketInitiator.readLoop()' has the wrong return type

어쨌든 나중에 작업을 시작하게하고 동시에 작업에서 발생한 예외를 추적합니까?

문제를 보여줄 수있는 최소한의 테스트 케이스 :https://dotnetfiddle.net/JIfDIn


  • 가능한 중복stackoverflow.com/questions/5383310/… - Peter Duniho
  • 가능한 중복stackoverflow.com/questions/17923204/… - Peter Duniho
  • 가능한 중복stackoverflow.com/questions/41742853/… - Peter Duniho
  • 결론은 여기 있습니다.async메소드 반환void, 이것은 호출 코드가 예외를 관찰하지 못하도록합니다.Task예외를 나타낼 수있는 메소드 너는 사용해서는 안된다.async void이런 맥락에서, 그렇게하지 않는 것이 바람직한 습관을 따랐 으면, 당신의 문제는 일어나지 않을 것입니다. 왜 반환 유형을 변경할 때 CS0407을 얻을 수 있습니까? 다른 곳에서도 실수를 저질렀어야했는데 좋은 물건을 제공하지 못했습니다.최소, 완료 및 확인 가능한 예제문제가 확실하게 재현되어 문제가 무엇인지 아무도 말할 수 없습니다. - Peter Duniho
  • @PeterDuniho는 Task ()가 첫 번째 인수로 Action을 필요로하기 때문에 컴파일되지 않으며, 단순히 void foo (void)입니다. Task foo ()를 수락하지 않습니다. - fluter

2 답변


2

만약 당신이 올바르게 이해한다면, 필요할 때까지 작업을 연기하고 싶지만 생성자에서 생성을 유지하고 싶을 것입니다. 괜찮습니다. 이러한 상황에 사용되는 패턴이 있습니다. 그것은 ~라고 불린다Lazy.

그러나 Lazy는 비동기 적이 아닙니다.그러나 Stephen Taub는 AsyncLazy라고하는 비동기 구현을 만들었습니다.

생성 한 외부 작업에 대한 반환 값을 지정하지 않았기 때문에 오류 메시지가 나타나는 이유가 있습니다. 또한 돌아올 필요가있다.foos 값. 내부 태스크를 반환하는 람다 (lambda)에서 호출을 랩핑하면 다음과 같이 얻을 수 있습니다.

var task1 = new Task<Task>(async () => await foo(),
     CancellationToken.None, TaskCreationOptions.LongRunning);
task1.Start();

그러나 당신이 그것을 기다리고있을 때 당신은 바깥의 일을 기다리고 그것을 돌려주는 일을 기다리지 않을 것이다. 따라서 그것을 언랩해야합니다 :

task1.Unwrap().Wait();

이렇게하면 예외를 잡을 수 있습니다. 하나,이것은 아마도가장 좋은 방법은 Task.Run, async 및 await을 사용하는 전체 이유가 이러한 구성을 피하는 것이었기 때문입니다. 결론적으로:

  • 작업 호출을 연기해야하는 경우 AsyncLazy로 이동하십시오.
  • Task 생성자를 호출하지 마십시오.
  • Task.Run 사용


  • 이것은 정말로 도움이됩니다, 감사합니다! - fluter
  • 질질 거리고 있지만, 저장하기 전에 포장을 푸는 것이 좋습니다.task1 - Kevin Gosse
  • @KevinGosse 그건 좋을 텐데, 그렇지만.Start()을 (를) 호출 할 수 없습니다. - Default
  • @Default 좋은 지적, 나쁘다. - Kevin Gosse

2

그리고 작업이 생성되는 즉시 실행될 필요가 없으므로 Task.Run 대신 Task 생성자를 사용하고 있습니다.

작업 생성자는 절대로 사용하지 않아야합니다.말 그대로 합리적인 유스 케이스가 있습니다.조금도.

귀하의 경우에는 위임이나 지연 초기화가 필요하지 않은 것처럼 들리지만, 멤버와 메서드는 비공개이므로 작업이 실행될 때 작업 멤버를 할당하는 것이 중요합니다.

private async Task readLoop();

// when the task needs to run:
readTask = Task.Run(() => readLoop());

그러나,해야 할 것미래에 실행될 코드를 표현해야하며 현재는 그렇지 않습니다. 그러면 대리인이 필요합니다. 이 경우,비동기 대리인:Func<Task>:

Func<Task> func = () => Task.Run(() => readLoop());
...
Task task = func();

연결된 질문


관련된 질문

최근 질문