C # ateş ve unut yöntemi yapmak için en basit yolu?


150

WCF'de bu [OperationContract(IsOneWay = true)]özelliklere sahip olduklarını gördüm . Ancak WCF, tıkanmasız bir işlev yaratmak için biraz yavaş ve ağır görünüyor. İdeal olarak statik boşluk MethodFoo(){}engelleme gibi bir şey olurdu , ama bunun var olduğunu düşünmüyorum.

C # 'da engellenmeyen bir yöntem çağrısı oluşturmanın en hızlı yolu nedir?

Örneğin

class Foo
{
    static void Main()
    {
        FireAway(); //No callback, just go away
        Console.WriteLine("Happens immediately");
    }

    static void FireAway()
    {
        System.Threading.Thread.Sleep(5000);
        Console.WriteLine("5 seconds later");
    }
}

Not : Bunu okuyan herkes, yöntemin gerçekten bitmesini isteyip istemediklerini düşünmelidir. (Bkz. # 2 üst yanıt) Yöntemin tamamlanması gerekiyorsa, ASP.NET uygulaması gibi bazı yerlerde iş parçacığını engellemek ve canlı tutmak için bir şeyler yapmanız gerekir. Aksi takdirde, bu "yangın unut-ama-asla-gerçekten-yürütmek" yol açabilir, bu durumda, elbette, hiç kod yazmak daha kolay olurdu. ( Bunun ASP.NET'te nasıl çalıştığının iyi bir açıklaması )

Yanıtlar:


273
ThreadPool.QueueUserWorkItem(o => FireAway());

(beş yıl sonra...)

Task.Run(() => FireAway());

luisperezphd tarafından işaret edildiği gibi .


Sen kazandın. Hayır, gerçekten, bunu başka bir işleve kapsüllemediğiniz sürece en kısa sürüm gibi görünüyor.
OregonGhost

13
Bunu düşünmek ... çoğu uygulamada bu iyidir, ancak konsolunuzda FireAway konsola bir şey göndermeden önce uygulama çıkacaktır. Havuz başlıkları arka plan iş parçacıklarıdır ve uygulama öldüğünde ölürler. Öte yandan, FireAway pencereye yazmaya çalışmadan önce konsol penceresi kaybolacağından bir iş parçacığı kullanmak da işe yaramaz.

3
Çalışması garanti edilen, engellemeyen bir yöntem çağrısına sahip olmanın bir yolu yoktur, bu nedenle bu aslında IMO sorusuna en doğru cevaptır. Yürütmeyi garanti etmeniz gerekiyorsa, AutoResetEvent (Kev'nın belirttiği gibi)
Guvante

1
İş parçacığı havuzundan bağımsız bir iş parçacığında çalıştırmak için, ben de gidebiliriz inanıyorum(new Action(FireAway)).BeginInvoke()
Sam Harwell

2
Bu konuda ne Task.Factory.StartNew(() => myevent());cevap den stackoverflow.com/questions/14858261/...
Luis Perez

39

C # 4.0 ve daha yeni sürümler için, en iyi cevabın şimdi Ade Miller tarafından verildiğine dikkat çekiyor: Bir yangın yapmanın ve c # 4.0'da yöntemi unutmanın en basit yolu

Task.Factory.StartNew(() => FireAway());

Ya da...

Task.Factory.StartNew(FireAway);

Veya...

new Task(FireAway).Start();

nerede FireAwayolduğunu

public static void FireAway()
{
    // Blah...
}

Sınıf ve yöntem adının tersliği sayesinde bu, seçtiğiniz konuya bağlı olarak threadpool sürümünü altı ila on dokuz karakter arasında yener :)

ThreadPool.QueueUserWorkItem(o => FireAway());

11
En iyi sorulara kolayca ulaşılabilecek en iyi yanıtları almakla ilgileniyorum. Kesinlikle başkalarını da aynısını yapmaya teşvik ediyorum.
Patrick Szalapski

3
Stackoverflow.com/help/referencing adresine göre , başka bir kaynaktan alıntı yaptığınızı belirtmek için blok alıntılar kullanmanız gerekir. Olduğu gibi cevabınız tamamen kendi işiniz gibi görünüyor. Yanıtın tam bir kopyasını yeniden göndererek amacınız bir yorumdaki bağlantıyla elde edilmiş olabilir.
tom redfern

1
parametreleri nasıl iletilir FireAway?
GuidoG

Seni ilk çözüm kullanmak gerekir inanıyoruz: Task.Factory.StartNew(() => FireAway(foo));.
Patrick Szalapski

1
Task.Factory.StartNew(() => FireAway(foo));foo kullanırken dikkatli olun, burada ve burada
AaA

22

.NET 4.5 için:

Task.Run(() => FireAway());

15
Fyi, bu genellikle blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx'yeTask.Factory.StartNew(() => FireAway(), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); göre kısadır
Jim Geurts

2
Bilmek güzel, @JimGeurts!
David Murdoch

Gelecek kuşakların çıkarına göre, yorumlarında @JimGeurts ile bağlantılı olan makale şimdi 404'tür (teşekkürler, MS!). Bu URL'deki tarih damgası göz önüne alındığında, Stephen Toub'un bu makalesi doğru gibi görünüyor - devblogs.microsoft.com/pfxteam/…
Dan Atkinson

1
@DanAtkinson haklısın. İşte MS'nin tekrar hareket etmesi durumunda archive.org'daki orijinal: web.archive.org/web/20160226022029/http://blogs.msdn.com/b/…
David Murdoch

18

Will'in cevabına eklemek için , bu bir konsol uygulamasıysa, işçi iş parçacığı tamamlanmadan önce çıkmasını önlemek için bir AutoResetEventve a atın WaitHandle:

Using System;
Using System.Threading;

class Foo
{
    static AutoResetEvent autoEvent = new AutoResetEvent(false);

    static void Main()
    {
        ThreadPoolQueueUserWorkItem(new WaitCallback(FireAway), autoEvent);
        autoEvent.WaitOne(); // Will wait for thread to complete
    }

    static void FireAway(object stateInfo)
    {
        System.Threading.Thread.Sleep(5000);
        Console.WriteLine("5 seconds later");
        ((AutoResetEvent)stateInfo).Set();
    }
}

Bu bir konsol uygulaması değilse ve C # sınıfım COM Görünürse, AutoEvent'iniz çalışır mı? AutoEvent.WaitOne () engelleniyor mu?
dan_l

5
@dan_l - Hiçbir fikrim yok, neden bunu yeni bir soru olarak sormuyorsunuz ve buna bir referansta bulunmuyorsunuz.
Kev

@dan_l, evet WaitOneher zaman engelliyor ve AutoResetEventher zaman işe yarayacak
AaA

AutoResetEvent burada yalnızca tek bir arka plan iş parçacığı varsa çalışır. Birden fazla iş parçacığı kullanıldığında bir Bariyerin daha iyi olup olmayacağını merak ediyorum. docs.microsoft.com/tr-tr/dotnet/standard/threading/barrier
Martin Brown

@MartinBrown - bunun doğru olduğundan emin değilim. WaitHandleHer biri kendi AutoResetEventve buna karşılık gelen bir s dizisine sahip olabilirsiniz ThreadPool.QueueUserWorkItem. Bundan sonra WaitHandle.WaitAll(arrayOfWaitHandles).
Kev

15

Kolay bir yol, parametresiz lambda ile bir iş parçacığı oluşturmak ve başlatmaktır:

(new Thread(() => { 
    FireAway(); 
    MessageBox.Show("FireAway Finished!"); 
}) { 
    Name = "Long Running Work Thread (FireAway Call)",
    Priority = ThreadPriority.BelowNormal 
}).Start();

Bu yöntemi ThreadPool.QueueUserWorkItem üzerinde kullanarak, hata ayıklamayı kolaylaştırmak için yeni iş parçacığınızı adlandırabilirsiniz. Ayrıca, bir hata ayıklayıcı dışındaki herhangi bir işlenmemiş istisna aniden uygulamanızı aniden çökeceğinden, rutininizde kapsamlı hata işlemeyi kullanmayı unutmayın:

resim açıklamasını buraya girin


Uzun süren veya engelleme işlemi nedeniyle özel bir iş parçacığına ihtiyacınız olduğunda +1.
Paul Turner

12

Asp.Net ve .Net 4.5.2 kullanırken bunu yapmanın önerilen yolu kullanmaktır QueueBackgroundWorkItem. İşte bir yardımcı sınıf:

public static class BackgroundTaskRunner
{     
    public static void FireAndForgetTask(Action action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2 required
        {
            try
            {
                action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }

    /// <summary>
    /// Using async
    /// </summary>
    public static void FireAndForgetTask(Func<Task> action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2 required
        {
            try
            {
                await action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }
}

Kullanım örneği:

BackgroundTaskRunner.FireAndForgetTask(() =>
{
    FireAway();
});

veya zaman uyumsuzluğu kullanarak:

BackgroundTaskRunner.FireAndForgetTask(async () =>
{
    await FireAway();
});

Bu Azure Web Sitelerinde harika çalışıyor.

Başvuru: .NET 4.5.2'de bir ASP.NET Uygulamasından Arka Plan İşlerini Zamanlamak için QueueBackgroundWorkItem Kullanma


7

BeginInvoke'u çağırmak ve EndInvoke'u yakalamamak iyi bir yaklaşım değildir. Yanıt basit: EndInvoke'u çağırmanızın nedeni, çağrının sonuçlarının (dönüş değeri olmasa bile) EndInvoke çağrılıncaya kadar .NET tarafından önbelleğe alınması gerektiğidir. Örneğin, çağrılan kod bir istisna atarsa, istisna çağrı verilerinde önbelleğe alınır. EndInvoke'u çağırıncaya kadar bellekte kalır. EndInvoke'u çağırdıktan sonra bellek serbest bırakılabilir. Bu özel durumda, veriler dahili olarak çağırma kodu ile korunduğu için işlem kapanana kadar bellek kalır. Sanırım GC nihayetinde toplayabilir ama GC'nin veriyi terk ettiğinizi nasıl bildiğini bilmiyorum, ancak bu veriyi almak çok uzun zaman alıyor. Şüpheliyim. Böylece bellek sızıntısı meydana gelebilir.

Daha fazlasını http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx adresinde bulabilirsiniz.


3
GC, herhangi bir referans yoksa şeylerin ne zaman terk edildiğini söyleyebilir. Eğer BeginInvokebuna bütün referanslar terk edildi kez getiriler bir başvuru tutan, ancak tarafından başvurulan olmayan bir geçiş nesnesi, gerçek sonuç verileri tutan nesne, sarıcı nesne sonuçlandırılması için uygun hale gelecektir.
supercat

Bunun "iyi bir yaklaşım" olmadığını söyleyerek; endişe duyduğunuz hata durumlarıyla test edin ve sorun olup olmadığına bakın. Çöp toplama mükemmel olmasa bile, muhtemelen çoğu uygulamayı belirgin şekilde etkilemez. Microsoft'a göre, "Gerekirse temsilci, dönüş değerini almak için EndInvoke çağırabilirsiniz, ancak bu gerekli değildir. EndInvoke, dönüş değeri alınana kadar engeller." from: msdn.microsoft.com/tr-tr/library/0b1bf3y3(v=vs.90).aspx
Abaküs

5

Neredeyse 10 yıl sonra:

Task.Run(FireAway);

FireAway içinde istisna işleme ve günlük kaydı eklerdim


3

En basit .NET 2.0 ve sonraki yaklaşım Asynchnonous Programming Model (Asynchnonous Programming Model) (yani bir delege üzerinde BeginInvoke) kullanmaktır:

static void Main(string[] args)
{
      new MethodInvoker(FireAway).BeginInvoke(null, null);

      Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);

      Thread.Sleep(5000);
}

private static void FireAway()
{
    Thread.Sleep(2000);

    Console.WriteLine("FireAway: " + Thread.CurrentThread.ManagedThreadId );  
}

Daha önce Task.Run()var olan, muhtemelen bunu yapmanın en kısa yoluydu.
binki
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.