웹 소켓에서 데이터를 수신하고 수정 한 다음 데이터 서비스에 업로드하는 네트워크 애플리케이션을 개발 중입니다. 데이터를 업로드하는 데 약간의 시간이 걸리며 한 번에 여러 메시지를 업로드하여 지연 시간을 숨기고 싶습니다. 업로드가 완료되면 웹 소켓을 통해 승인을 다시 보내야합니다.
나는 모든 미해결 작업을 보관하기 위해 작업 대기열을 만들었습니다. 이것은 잘 작동하는 것 같아서 포함하지 않았습니다. 하지만 내 업로드 작업이 실제로 완료되기 전에 완료되는 것 같습니다. 다음은 잘린 샘플입니다.
private async Task UploadDataAsync(string data, CancellationToken cancellationToken)
{
Task uploadTask = new Task(async () =>
{
// Simulates uploading data
await Task.Delay(5000, cancellationToken);
});
_ = uploadTask.ContinueWith(async (t1) =>
{
// Clean up the task
await RemoveTask(t1);
if (t1.IsCompletedSuccessfully)
await SendAck(this, data, cancellationToken);
else if (t1.IsFaulted)
logger.LogError(t1.Exception, $"An error occurred while uploading {data}");
else if (t1.IsCanceled)
logger.LogWarning($"An upload task {data} was canceled");
}, TaskScheduler.Default);
await AddTask(uploadTask);
uploadTask.Start();
}
앞의 코드에서는 데이터가 업로드되기 전에 승인이 전송됩니다. 놀랍게도 이것은 내 작업이 비동기 람다를 사용하기 때문인 것 같습니다. 이유 때문에 uploadTask
업로드가 기다리고있을 때 완료되는 것을 이해하지 못합니다 . 그래서 다음과 같이 변경했습니다.
private async Task UploadDataAsync(string data, CancellationToken cancellationToken)
{
Task uploadTask = new Task(() =>
{
// Simulates uploading data
Task.Delay(5000, cancellationToken).Wait();
});
_ = uploadTask.ContinueWith((t1) =>
{
// Clean up the task
RemoveTask(t1).Wait();
if (t1.IsCompletedSuccessfully)
SendAck(this, data, cancellationToken).Wait();
else if (t1.IsFaulted)
logger.LogError(t1.Exception, $"An error occurred while uploading {data}");
else if (t1.IsCanceled)
logger.LogWarning($"An upload task {data} was canceled");
}, TaskScheduler.Default);
await AddTask(uploadTask);
uploadTask.Start();
}
이제 일이 잘못되거나 작업이 취소 된 경우 (예 : 서버가 종료 됨)를 제외하고 모든 것이 올바른 순서로 실행됩니다. 이제 AggregateExceptions 및 TaskCanceledExceptions를 처리하고 있습니다.
이것은 내가 만드는 것보다 쉬울 것 같습니다. 내가 잘못하고 있니?
편집UploadDataAsync
컨텍스트 를 호출 하는 의사 코드 추가 .
protected override async Task DoConnection(CancellationToken cancellationToken)
{
while (_websocket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)
{
// Simulates getting websocket data
string result = await _websocket.ReceiveAsync();
// This should only asynchronously wait for the upload task to get
// created. It should not wait for the upload to complete.
await OnDataReceived(result, cancellationToken);
}
}
Task
비동기 델리게이트가 있는 생성자 의 문제는 델리게이트의 서명이 async void
대신으로 확인되어 async Task
비동기 작업을 기다릴 수 없다는 것입니다 ( async void
주로 이벤트 처리기에 유용하고 대부분의 다른 경우에 유해 함). 이는 Task
생성자가 비동기 대리자를 이해하지 못하기 때문에 발생합니다 Func<Task>
. 즉, 인수 를 허용하는 오버로드가 없음을 의미합니다 .
Task
솔루션 에서 생성자를 제거하지 않고도이 문제를 해결할 수있는 방법이 있습니다 . 대신 제네릭이 아닌의 Task
당신은 제네릭을 사용할 수 있습니다 Task<TResult>
(가)와, TResult
인 Task
. 즉, 중첩 된 Task<Task>
. 외부 작업의 역할은 제공된 비동기 대리자를 실행하여 내부 작업을 만드는 것입니다. 이것은 대부분의 경우 매우 짧은 기간을 갖는 CPU 바운드 작업입니다. 기본적으로 외부 작업은 코드가 await
비동기 델리게이트 의 첫 번째 ¹에 도달 하자마자 완료되고 나머지 작업 (대기 시간이 긴 I / O 작업 포함)은 내부 작업으로 표시됩니다.
Task<Task> uploadTaskFactory = new Task<Task>(async () =>
{
await Task.Delay(5000, cancellationToken); // Simulates an I/O operation
});
//...
uploadTaskFactory.Start();
Task uploadTask = await uploadTaskFactory; // takes essentially zero time
//...
await uploadTask; // takes 5 seconds
Task<Task>
비동기 델리게이트와 함께 생성자를 사용하는 것은 다소 어색해 지므로 일반적으로 대체 방법을 선호해야합니다 (특히 라이브러리의 경우 애플리케이션 코드를 작성할 때 IMHO 괜찮음). 대안으로는 Task.Run
비동기 델리게이트를 이해하거나 작업을 즉시 시작하지 않으려는 경우 Func<Task>
s 를 전달 하고 적절한 순간에 호출하는 것이 포함됩니다.
¹ 정확히 말하면 await
완료되지 않은 기다림 중 첫 번째 입니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다