이 질문이 정말 마음에 듭니다.
C #에서 화재를 수행하고 메서드를 잊어 버리는 가장 간단한 방법은 무엇입니까?
이제 C # 4.0에 Parallel 확장이 있으므로 Parallel linq로 Fire & Forget을 수행하는 더 깨끗한 방법이 있다는 것을 알고 싶습니다.
이 질문이 정말 마음에 듭니다.
C #에서 화재를 수행하고 메서드를 잊어 버리는 가장 간단한 방법은 무엇입니까?
이제 C # 4.0에 Parallel 확장이 있으므로 Parallel linq로 Fire & Forget을 수행하는 더 깨끗한 방법이 있다는 것을 알고 싶습니다.
답변:
4.0에 대한 답은 아니지만 .Net 4.5에서는 다음과 같이 더 간단하게 만들 수 있습니다.
#pragma warning disable 4014
Task.Run(() =>
{
MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
pragma는이 작업을 실행 중임을 알리는 경고를 비활성화하는 것입니다.
중괄호 안의 메서드가 Task를 반환하는 경우 :
#pragma warning disable 4014
Task.Run(async () =>
{
await MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
그것을 분해 해보자 :
Task.Run은이 코드가 백그라운드에서 실행될 것이라는 컴파일러 경고 (경고 CS4014)를 생성하는 Task를 반환합니다.이 코드는 정확히 사용자가 원했던 것이므로 경고 4014를 비활성화합니다.
기본적으로 태스크는 "원래 스레드로 다시 마샬링"하려고 시도합니다. 즉,이 태스크는 백그라운드에서 실행 된 다음 시작된 스레드로 돌아 가려고 시도합니다. 종종 원래 스레드가 완료된 후 작업을 실행하고 잊어 버립니다. 그러면 ThreadAbortException이 throw됩니다. 대부분의 경우 이것은 무해합니다. 그것은 단지 당신에게 말하고, 다시 합류하려했지만 실패했지만 당신은 어쨌든 상관하지 않습니다. 그러나 Production의 로그 또는 로컬 dev의 디버거에 ThreadAbortExceptions가있는 것은 여전히 약간 시끄 럽습니다. .ConfigureAwait(false)
깔끔하게 유지하고 명시 적으로 말하고 백그라운드에서 실행하는 방법 일뿐입니다. 그게 다입니다.
이것은 장황함, 특히 추악한 pragma이므로이를 위해 라이브러리 메서드를 사용합니다.
public static class TaskHelper
{
/// <summary>
/// Runs a TPL Task fire-and-forget style, the right way - in the
/// background, separate from the current thread, with no risk
/// of it trying to rejoin the current thread.
/// </summary>
public static void RunBg(Func<Task> fn)
{
Task.Run(fn).ConfigureAwait(false);
}
/// <summary>
/// Runs a task fire-and-forget style and notifies the TPL that this
/// will not need a Thread to resume on for a long time, or that there
/// are multiple gaps in thread use that may be long.
/// Use for example when talking to a slow webservice.
/// </summary>
public static void RunBgLong(Func<Task> fn)
{
Task.Factory.StartNew(fn, TaskCreationOptions.LongRunning)
.ConfigureAwait(false);
}
}
용법:
TaskHelper.RunBg(async () =>
{
await doSomethingAsync();
}
ConfigureAwait(false)
에 Task.Run
당신이하지 않는 경우 await
작업을. 함수의 목적은 이름에 있습니다 : "configure await ". 그렇게하지 않으면 await
작업을, 당신은 계속를 등록하지 않으며, 당신이 말한대로 "원래 스레드 위에 정렬 화 다시"에 작업에 대한 코드는 존재하지 않는다. 더 큰 위험은 종료 자 스레드에서 관찰되지 않은 예외가 다시 발생하는 것입니다.
#Disable Warning BC42358
Task
yes 클래스를 사용하면 PLINQ는 실제로 컬렉션을 쿼리하는 데 사용됩니다.
다음과 같은 작업이 작업으로 수행됩니다.
Task.Factory.StartNew(() => FireAway());
또는...
Task.Factory.StartNew(FireAway);
또는...
new Task(FireAway).Start();
어디 FireAway
있다
public static void FireAway()
{
// Blah...
}
따라서 클래스 및 메서드 이름 간결성으로 인해 선택한 항목에 따라 6 ~ 19 개의 문자가 스레드 풀 버전을 능가합니다. :)
ThreadPool.QueueUserWorkItem(o => FireAway());
fire and forget in ASP.NET WebForms and windows.close()
?
이 질문에 대한 주요 답변에 몇 가지 문제가 있습니다.
첫째, 진정한 화재 후 잊기 상황에서는 await
작업을 수행 하지 않을 것이므로 ConfigureAwait(false)
. 에서 await
반환 한 값 이 없으면 ConfigureAwait
효과가 없을 수 있습니다.
둘째, 작업이 예외와 함께 완료 될 때 어떤 일이 발생하는지 알아야합니다. @ ade-miller가 제안한 간단한 솔루션을 고려하십시오.
Task.Factory.StartNew(SomeMethod); // .NET 4.0
Task.Run(SomeMethod); // .NET 4.5
이 소개하는 위험 : 처리되지 않은 예외가에서 탈출하는 경우 SomeMethod()
, 그 예외는 관찰되지 될 않을 수도 1 BE 재 throw 종료 자 스레드에서, 응용 프로그램 충돌. 따라서 결과 예외가 관찰되는지 확인하기 위해 도우미 메서드를 사용하는 것이 좋습니다.
다음과 같이 작성할 수 있습니다.
public static class Blindly
{
private static readonly Action<Task> DefaultErrorContinuation =
t =>
{
try { t.Wait(); }
catch {}
};
public static void Run(Action action, Action<Exception> handler = null)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
var task = Task.Run(action); // Adapt as necessary for .NET 4.0.
if (handler == null)
{
task.ContinueWith(
DefaultErrorContinuation,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
else
{
task.ContinueWith(
t => handler(t.Exception.GetBaseException()),
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.OnlyOnFaulted);
}
}
}
이 구현에는 최소한의 오버 헤드가 있어야합니다. 연속은 작업이 성공적으로 완료되지 않은 경우에만 호출되며 동 기적으로 호출되어야합니다 (원래 작업과 별도로 예약되는 것과 반대). "게으른"경우에는 연속 위임에 대한 할당도 발생하지 않습니다.
비동기 작업을 시작하는 것은 간단합니다.
Blindly.Run(SomeMethod); // Ignore error
Blindly.Run(SomeMethod, e => Log.Warn("Whoops", e)); // Log error
1. 이것은 .NET 4.0의 기본 동작입니다. .NET 4.5에서는 관찰되지 않은 예외가 종료 자 스레드에서 다시 발생하지 않도록 기본 동작이 변경되었습니다 (TaskScheduler의 UnobservedTaskException 이벤트를 통해 계속 관찰 할 수 있음). 그러나 기본 구성을 재정의 할 수 있으며 애플리케이션에 .NET 4.5가 필요한 경우에도 관찰되지 않은 작업 예외가 무해하다고 가정해서는 안됩니다.
ContinueWith
취소로 인해이 호출 된 경우 선행 항목의 예외 속성은 Null이되고 null 참조 예외가 throw됩니다. 작업 계속 옵션을로 설정하면 OnlyOnFaulted
null 검사가 필요하지 않거나 사용하기 전에 예외가 null인지 확인합니다.
Task
이 구현 세부 정보가됩니다.
Task
"는 "백그라운드 작업을 시작하는 간단한 방법을 원하고 그 결과를 관찰하지 않으며 어떤 메커니즘이 사용되는지 상관하지 않습니다."와 동일하지 않습니다. 할 수 있습니다. " 이를 바탕으로 나는 질문에 대한 나의 대답 을지지합니다 .
fire and forget
에서 ASP.NET 웹폼 과 windows.close()
?
Mike Strobel의 답변에서 발생할 몇 가지 문제를 해결하기 위해 :
var task = Task.Run(action)
해당 작업에 연속을 사용 하고 할당 한 후 Task
예외 처리기 연속을 .NET Framework에 할당하기 전에 예외 가 발생할 위험 이 Task
있습니다. 따라서 아래 클래스는 이러한 위험이 없어야합니다.
using System;
using System.Threading.Tasks;
namespace MyNameSpace
{
public sealed class AsyncManager : IAsyncManager
{
private Action<Task> DefaultExeptionHandler = t =>
{
try { t.Wait(); }
catch { /* Swallow the exception */ }
};
public Task Run(Action action, Action<Exception> exceptionHandler = null)
{
if (action == null) { throw new ArgumentNullException(nameof(action)); }
var task = new Task(action);
Action<Task> handler = exceptionHandler != null ?
new Action<Task>(t => exceptionHandler(t.Exception.GetBaseException())) :
DefaultExeptionHandler;
var continuation = task.ContinueWith(handler,
TaskContinuationOptions.ExecuteSynchronously
| TaskContinuationOptions.OnlyOnFaulted);
task.Start();
return continuation;
}
}
}
여기서는 task
직접 실행되지 않고 대신 생성되고 연속이 할당 된 다음에 만 작업이 실행되어 연속을 할당하기 전에 작업이 실행을 완료 (또는 일부 예외 발생) 할 위험을 제거합니다.
Run
방법은 여기에 지속을 반환 Task
나는 확실히 실행이 완료하고 단위 테스트를 작성 할 수 있어요 그래서. 하지만 사용시 무시해도됩니다.