source

ASP.NET MVC에서 비동기 메서드를 실행하고 잊습니다.

lovecheck 2023. 7. 1. 08:59
반응형

ASP.NET MVC에서 비동기 메서드를 실행하고 잊습니다.

여기여기와 같이 질문을 시작하고 잊어버리는 것에 대한 일반적인 대답은 비동기/대기를 사용하는 것이 아니라 를 사용하는 것입니다.Task.Run또는TaskFactory.StartNew대신 동기식 방법을 전달합니다.
그러나 때때로 내가 실행하고 싶은 방법은 비동기식이며 동일한 동기화 방법이 없습니다.

업데이트 참고/경고: Stephen Cleary가 아래에서 지적했듯이, 응답을 보낸 후 요청 작업을 계속하는 것은 위험합니다.작업이 진행 중인 동안 앱도메인이 종료될 수 있기 때문입니다.자세한 내용은 그의 응답 링크를 참조하십시오.어쨌든, 저는 누군가를 잘못된 길로 보내지 않기 위해 그것을 먼저 지적하고 싶었습니다.

실제 작업은 다른 시스템(다른 서버에 있는 다른 컴퓨터)에서 수행되므로 해당 시스템에 대한 메시지만 남겼기 때문에 제 사례가 유효하다고 생각합니다.서버 또는 사용자가 수행할 수 있는 예외가 없고 사용자에게 영향을 미치지 않는 경우 예외 로그를 참조하여 수동으로 정리하거나 일부 자동화된 메커니즘을 구현하기만 하면 됩니다.AppDomain이 종료되면 원격 시스템에 남아 있는 파일이 남아 있지만, 일반적인 유지 관리 주기의 일부로 이 파일을 선택할 것입니다. 이 파일은 웹 서버(데이터베이스)에서 더 이상 존재를 알 수 없고 이름이 고유한 타임스탬프로 지정되어 있기 때문에 계속 유지되는 동안 문제가 발생하지 않습니다.

Stephen Cleary가 지적한 것처럼 지속성 메커니즘에 접근할 수 있다면 이상적이겠지만, 안타깝게도 지금은 그렇지 않습니다.

Delete Foo 요청을 오픈 상태로 유지하면서 클라이언트 측(javascript)에서 정상적으로 완료한 것처럼 생각했는데, 계속 진행하려면 응답에 정보가 필요해서 지연될 것 같습니다.

그래서, 원래 질문은...

예:

//External library
public async Task DeleteFooAsync();

제 asp.net mvc 코드에서 저는 DeleteFooAsync를 화재 및 폭발 방식으로 부르고 싶습니다. DeleteFooAsync가 완료될 때까지 응답을 보류하고 싶지 않습니다.DeleteFooAsync가 어떤 이유로 실패하거나 예외를 발생시키는 경우 사용자나 프로그램에서 수행할 수 있는 작업이 없으므로 오류를 기록하고 싶습니다.

모든 예외가 관찰되지 않은 예외를 초래한다는 것을 알고 있습니다. 따라서 제가 생각할 수 있는 가장 간단한 경우는 다음과 같습니다.

//In my code
Task deleteTask = DeleteFooAsync()

//In my App_Start
TaskScheduler.UnobservedTaskException += ( sender, e ) =>
{
    m_log.Debug( "Unobserved exception! This exception would have been unobserved: {0}", e.Exception );
    e.SetObserved();
};

이것을 하는 데 위험이 있습니까?

제가 생각할 수 있는 다른 옵션은 다음과 같은 저만의 포장지를 만드는 것입니다.

private void async DeleteFooWrapperAsync()
{
    try
    {
        await DeleteFooAsync();
    }
    catch(Exception exception )
    {
        m_log.Error("DeleteFooAsync failed: " + exception.ToString());
    }
}

TaskFactory에 연락해 주세요.새로 시작(비동기 동작으로 감쌀 수 있음).하지만 이것은 제가 방화와 망각 방식으로 비동기 방식을 부르고 싶을 때마다 많은 래퍼 코드처럼 보입니다.

제 질문은, 비동기식을 화재-잊음 방식으로 부르는 올바른 방법이 무엇인가 하는 것입니다.

업데이트:

컨트롤러에서 다음을 발견했습니다(대기 중인 다른 비동기 호출이 있기 때문에 컨트롤러 작업이 비동기일 필요는 없습니다).

[AcceptVerbs( HttpVerbs.Post )]
public async Task<JsonResult> DeleteItemAsync()
{
    Task deleteTask = DeleteFooAsync();
    ...
}

다음 형식의 예외를 발생시켰습니다.

처리되지 않은 예외:시스템.Null 참조 예외:개체 참조가 개체의 인스턴스로 설정되지 않았습니다.시스템에서.웹.스레드 컨텍스트.연관짓다전류 포함스레드(Booleanset ImpersonationContext)

이 문제는 여기서 설명하며 동기화 컨텍스트 및 '반환된 작업이 모든 비동기 작업이 완료되기 전에 터미널 상태로 전환되었습니다'와 관련된 것으로 보입니다.

효과가 있었던 유일한 방법은 다음과 같습니다.

Task foo = Task.Run( () => DeleteFooAsync() );

StartNew는 DeleteFooAsync에서 작업할 새 스레드를 얻기 때문에 이 작업이 가능합니다.

슬프게도, 아래 Scott의 제안은 이 경우 예외를 처리하는 데 효과적이지 않습니다. 왜냐하면 foo는 더 이상 DeleteFooAsync 작업이 아니라 Task의 작업이기 때문입니다.실행합니다. 따라서 DeleteFooAsync의 예외를 처리하지 않습니다.관찰되지 않은 작업 예외가 결국 호출되므로 적어도 여전히 작동합니다.

그렇다면, asp.net mvc에서 어떻게 비동기식 방법을 실행하고 실행할 수 있는지에 대한 질문은 여전히 유효하다고 생각합니다.

먼저 ASP.NET 응용 프로그램에서 "fire and forget"은 거의 항상 실수라는 점을 지적하겠습니다."Fire and Forget"은 고객이 고객의 요구사항을 충족하는지 여부에 상관하지 않는 경우에만 허용되는 접근 방식입니다.DeleteFooAsync실제로 완료됩니다.

만약 당신이 그 제한을 받아들이려 한다면, 제 블로그에 ASP.NET 런타임에 작업을 등록할 코드가 있고, 그것은 동기식 작업과 비동기식 작업을 모두 허용합니다.

다음과 같은 예외를 로깅하기 위한 일회성 래퍼 방법을 작성할 수 있습니다.

private async Task LogExceptionsAsync(Func<Task> code)
{
  try
  {
    await code();
  }
  catch(Exception exception)
  {
    m_log.Error("Call failed: " + exception.ToString());
  }
}

그리고 나서 사용합니다.BackgroundTaskManager내 블로그에서 다음과 같이.

BackgroundTaskManager.Run(() => LogExceptionsAsync(() => DeleteFooAsync()));

또는 다음을 수행할 수 있습니다.TaskScheduler.UnobservedTaskException그냥 이렇게 불러요.

BackgroundTaskManager.Run(() => DeleteFooAsync());

.NET 4.5.2 기준으로 다음을 수행할 수 있습니다.

HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync());

그러나 ASP.NET 도메인 내에서만 작동합니다.

호스팅 환경.QueueBackgroundWorkItem 메서드를 사용하면 작은 백그라운드 작업 항목을 예약할 수 있습니다. ASP.NET은 이러한 항목을 추적하여 모든 백그라운드 작업 항목이 완료될 때까지 IIS가 작업자 프로세스를 갑자기 종료하지 못하도록 합니다.이 메서드는 ASP.NET 관리 앱 도메인 외부에서 호출할 수 없습니다.

많은 정보: https://msdn.microsoft.com/en-us/library/ms171868(v=vs.110).aspx#v452

메소드를 사용하고 옵션을 전달하는 것이 가장 좋습니다.

private void button1_Click(object sender, EventArgs e)
{
    var deleteFooTask = DeleteFooAsync();
    deleteFooTask.ContinueWith(ErrorHandeler, TaskContinuationOptions.OnlyOnFaulted);
}

private void ErrorHandeler(Task obj)
{
    MessageBox.Show(String.Format("Exception happened in the background of DeleteFooAsync.\n{0}", obj.Exception));
}

public async Task DeleteFooAsync()
{
    await Task.Delay(5000);
    throw new Exception("Oops");
}

내가 메시지 상자를 둔 곳에 당신은 로거를 두었습니다.

언급URL : https://stackoverflow.com/questions/18502745/fire-and-forget-async-method-in-asp-net-mvc

반응형