Bu soruyu gerçekten beğendim:
C # ile ateş et ve unut yöntemini yapmanın en basit yolu?
Artık C # 4.0'da Paralel uzantılarımız olduğuna göre, Paralel linq ile Fire & Forget yapmanın daha temiz bir yolu var mı?
Bu soruyu gerçekten beğendim:
C # ile ateş et ve unut yöntemini yapmanın en basit yolu?
Artık C # 4.0'da Paralel uzantılarımız olduğuna göre, Paralel linq ile Fire & Forget yapmanın daha temiz bir yolu var mı?
Yanıtlar:
4.0 için bir yanıt değil, ancak .Net 4.5'te bunu aşağıdakilerle daha da basitleştirebileceğinizi belirtmek gerekir:
#pragma warning disable 4014
Task.Run(() =>
{
MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
Pragma, size bu Görevi ateş ve unut olarak çalıştırdığınızı söyleyen uyarıyı devre dışı bırakmaktır.
Küme parantezlerinin içindeki yöntem bir Görev döndürürse:
#pragma warning disable 4014
Task.Run(async () =>
{
await MyFireAndForgetMethod();
}).ConfigureAwait(false);
#pragma warning restore 4014
Bunu parçalayalım:
Task.Run, bu kodun arka planda çalıştırılacağını belirten bir derleyici uyarısı (CS4014 uyarısı) oluşturan bir Task döndürür - tam olarak istediğiniz buydu, bu yüzden 4014 uyarısını devre dışı bırakıyoruz.
Varsayılan olarak, Görevler "Orijinal İş Parçacığına geri dönmeye" çalışır, bu da bu Görevin arka planda çalışacağı ve ardından onu başlatan İş Parçacığına geri dönmeye çalışacağı anlamına gelir. Orijinal İş Parçacığı tamamlandıktan sonra genellikle ateşleyin ve Görevler biter. Bu bir ThreadAbortException oluşturulmasına neden olur. Çoğu durumda bu zararsızdır - sadece size söylüyorum, yeniden katılmaya çalıştım, başarısız oldum, ama yine de umursamıyorsun. Ancak ThreadAbortExceptions'ın Üretimdeki günlüklerinizde veya yerel geliştirmedeki hata ayıklayıcınızda olması hala biraz gürültülü. .ConfigureAwait(false)
bu sadece düzenli kalmanın bir yoludur ve açıkça şunu söyleyin, bunu arka planda çalıştırın ve hepsi bu.
Bu çok sözlü olduğu için, özellikle çirkin pragma, bunun için bir kütüphane yöntemi kullanıyorum:
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);
}
}
Kullanım:
TaskHelper.RunBg(async () =>
{
await doSomethingAsync();
}
ConfigureAwait(false)
üzerinde Task.Run
değil mi zaman await
görev. Fonksiyonun amacı kendi adındadır: "configure await ". Bunu yapmazsanız await
görevi, bir devamı kayıt yoktur ve dediğiniz gibi "orijinal iplik üzerine Mareşal arkasına" için görev için hiçbir kod yoktur. Daha büyük risk, gözlemlenmemiş bir istisnanın sonlandırıcı iş parçacığında yeniden ortaya çıkmasıdır, ki bu yanıt bu yanıtın bile değinmiyor.
#Disable Warning BC42358
İle Task
sınıf evet, ama PLINQ koleksiyonları üzerinde sorgulamak için gerçekten.
Aşağıdakine benzer bir şey bunu Görev ile yapacaktır.
Task.Factory.StartNew(() => FireAway());
Ya da...
Task.Factory.StartNew(FireAway);
Veya...
new Task(FireAway).Start();
nerede FireAway
olduğunu
public static void FireAway()
{
// Blah...
}
Dolayısıyla, sınıf ve yöntem adı kısalığı sayesinde bu, iş parçacığı sürümünü, seçtiğiniz karaktere bağlı olarak altı ila on dokuz karakter arasında yener :)
ThreadPool.QueueUserWorkItem(o => FireAway());
fire and forget in ASP.NET WebForms and windows.close()
?
Bu sorunun ana cevabıyla ilgili birkaç sorunum var.
Birincisi, gerçek bir ateş ve unut durumunda, muhtemelen await
görevi yapmayacaksınız , bu yüzden eklemek işe yaramaz ConfigureAwait(false)
. await
Tarafından döndürülen değeri almazsanız ConfigureAwait
, muhtemelen herhangi bir etkisi olamaz.
İkinci olarak, görev bir istisna ile tamamlandığında ne olacağını bilmeniz gerekir. @ Ade-miller'in önerdiği basit çözümü düşünün:
Task.Factory.StartNew(SomeMethod); // .NET 4.0
Task.Run(SomeMethod); // .NET 4.5
Bu bir tehlike oluşturur: eğer işlenmemiş bir istisna kaçarsa SomeMethod()
, bu istisna asla gözlenmez ve sonlandırıcı iş parçacığına yeniden atılarak uygulamanızın kilitlenmesi 1 olabilir. Bu nedenle, ortaya çıkan istisnaların gözlemlenmesini sağlamak için yardımcı bir yöntem kullanmanızı tavsiye ederim.
Bunun gibi bir şey yazabilirsiniz:
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);
}
}
}
Bu uygulama minimum ek yüke sahip olmalıdır: Devam etme yalnızca görev başarıyla tamamlanmazsa başlatılır ve eşzamanlı olarak çağrılmalıdır (orijinal görevden ayrı olarak planlanmasının aksine). "Tembel" durumda, devam temsilcisi için bir tahsisat bile yapmazsınız.
Zaman uyumsuz bir işlemi başlatmak daha sonra önemsiz hale gelir:
Blindly.Run(SomeMethod); // Ignore error
Blindly.Run(SomeMethod, e => Log.Warn("Whoops", e)); // Log error
1. Bu, .NET 4.0'daki varsayılan davranıştır. .NET 4.5, varsayılan davranış gözlemlenmemiş istisnalar olur, öyle ki değiştirildi değil (hala TaskScheduler üzerinde UnobservedTaskException olayı aracılığıyla onları gözlemlemek olabilir) sonlandırıcıyı iş parçacığı üzerinde rethrown olun. Ancak, varsayılan yapılandırma geçersiz kılınabilir ve uygulamanız .NET 4.5 gerektirse bile, gözlenmeyen görev istisnalarının zararsız olacağını varsaymamalısınız.
ContinueWith
iptal nedeniyle çağrılırsa, öncülün istisna özelliği Null olur ve bir boş referans istisnası atılır. Görev devam ettirme seçeneğini olarak ayarlamak, OnlyOnFaulted
boş kontrol ihtiyacını ortadan kaldırır veya kullanmadan önce istisnanın boş olup olmadığını kontrol eder.
Task
bir uygulama ayrıntısı haline gelir.
Task
" ile aynı şey değildir "Arka plan işlemini başlatmak için basit bir yol istiyorum, sonucunu asla gözlemlemiyorum ve hangi mekanizmanın kullanıldığı umurumda değil yapmak için." Bu temelde, sorulan soruya verdiğim cevabın arkasında duruyorum .
fire and forget
in ASP.NET WebForms ve windows.close()
?
Mike Strobel'in cevabıyla ortaya çıkacak bazı sorunları düzeltmek için:
Bu var task = Task.Run(action)
göreve bir devam ettirme kullanırsanız ve sonra atarsanız, o zaman Task
bir istisna işleyicisi devamı atamadan önce bazı istisna atma riskiyle karşılaşırsınız Task
. Dolayısıyla, aşağıdaki sınıf bu riskten arındırılmış olmalıdır:
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;
}
}
}
Burada, task
doğrudan çalıştırılmaz, bunun yerine yaratılır, bir devam atanır ve ancak o zaman görev, bir devam atamadan önce görevin yürütmeyi tamamlama (veya bazı istisna atma) riskini ortadan kaldırmak için çalıştırılır.
Buradaki Run
yöntem devamı döndürür, Task
böylece yürütmenin tamamlandığından emin olarak birim testleri yazabilirim. Yine de kullanımınızda güvenle görmezden gelebilirsiniz.