सी # में एक आग और भूलने की विधि करने का सबसे सरल तरीका?


150

मैंने डब्ल्यूसीएफ में देखा कि उनके पास क्या [OperationContract(IsOneWay = true)]विशेषता है। लेकिन WCF एक नॉनब्लॉकिंग फंक्शन बनाने के लिए सिर्फ धीमा और भारी लगता है। आदर्श रूप से स्थैतिक शून्य गैर-संपर्क जैसा कुछ होगा MethodFoo(){}, लेकिन मुझे नहीं लगता कि यह मौजूद है।

C # में नॉनब्लॉकिंग मेथड कॉल करने का सबसे तेज तरीका क्या है?

उदाहरण के लिए

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");
    }
}

एनबी : इसे पढ़ने वाले सभी को यह सोचना चाहिए कि क्या वे वास्तव में खत्म करने की विधि चाहते हैं। (# 2 शीर्ष उत्तर देखें) यदि विधि को समाप्त करना है, तो कुछ स्थानों पर, ASP.NET एप्लिकेशन की तरह, आपको थ्रेड को ब्लॉक करने और जीवित रखने के लिए कुछ करना होगा। अन्यथा, यह "आग-भूल-लेकिन-कभी-कभी-वास्तव में निष्पादित करने के लिए" हो सकता है, जिस स्थिति में, निश्चित रूप से, कोई कोड नहीं लिखना सरल होगा। ( यह कैसे ASP.NET में काम करता है का एक अच्छा विवरण )

जवाबों:


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

(पाँच साल बाद...)

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

जैसा कि luisperezphd ने बताया है ।


तुम जीते। नहीं, वास्तव में, यह सबसे छोटा संस्करण प्रतीत होता है, जब तक कि आप इसे दूसरे फ़ंक्शन में एनकैप्सुलेट नहीं करते।
ओरेगनगॉस्ट

13
इस बारे में सोच ... अधिकांश अनुप्रयोगों में यह ठीक है, लेकिन आपके अंदर कंसोल ऐप से बाहर निकल जाएगा इससे पहले कि फ़ायरवॉल कंसोल को कुछ भी भेजता है। थ्रेडपूल थ्रेड बैकग्राउंड थ्रेड होते हैं और ऐप के मरने पर मर जाते हैं। दूसरी ओर, थ्रेड का उपयोग करने से या तो काम नहीं करेगा क्योंकि फायरवाइस विंडो में लिखने की कोशिश करने से पहले कंसोल विंडो गायब हो जाएगी।

3
गैर-अवरोधक विधि कॉल करने का कोई तरीका नहीं है जो चलाने की गारंटी है, इसलिए यह वास्तव में प्रश्न IMO का सबसे सटीक उत्तर है। यदि आपको निष्पादन की गारंटी देने की आवश्यकता है, तो संभावित अवरोध को एक नियंत्रण संरचना जैसे कि AutoResetEvent (केव के रूप में उल्लिखित) के माध्यम से पेश करने की आवश्यकता है
Guvante

1
थ्रेड पूल से स्वतंत्र थ्रेड में कार्य को चलाने के लिए, मेरा मानना ​​है कि आप भी जा सकते हैं(new Action(FireAway)).BeginInvoke()
सैम हार्वेल

2
क्या इस बारे में Task.Factory.StartNew(() => myevent());जवाब से stackoverflow.com/questions/14858261/…
लुइस पेरेज़

39

C # 4.0 और नए के लिए, यह मुझे बताता है कि अब सबसे अच्छा जवाब Ade मिलर द्वारा दिया गया है: c # 4.0 में आग लगाने और भूलने का सबसे सरल तरीका

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

या और भी...

Task.Factory.StartNew(FireAway);

या ...

new Task(FireAway).Start();

कहाँ FireAwayहै

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

तो वर्ग और विधि नाम के गुण के आधार पर यह आपके द्वारा चुने गए के आधार पर छह और उन्नीस वर्णों के बीच थ्रेडपूल संस्करण को धड़कता है :)

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

11
मुझे सबसे अच्छे सवालों के आसानी से उपलब्ध होने में सबसे ज्यादा दिलचस्पी है। मैं निश्चित रूप से दूसरों को भी ऐसा करने के लिए प्रोत्साहित करूंगा।
पैट्रिक शाजापस्की

3
के अनुसार stackoverflow.com/help/referencing , आप संकेत मिलता है कि आप किसी अन्य स्रोत से हवाले से कर रहे हैं blockquotes उपयोग करने के लिए आवश्यक हैं। जैसा कि यह आपका उत्तर है कि आपके सभी कार्य स्वयं प्रतीत होते हैं। उत्तर की एक सटीक प्रति फिर से पोस्ट करके आपका इरादा एक टिप्पणी में एक लिंक के साथ हासिल किया जा सकता था।
टॉम रेड

1
कैसे मापदंडों को पारित करने के लिए FireAway?
गुइडो जी

मेरा मानना ​​है कि आपको पहले समाधान का उपयोग करना होगा Task.Factory.StartNew(() => FireAway(foo));:।
पैट्रिक सज्जापस्की

1
Task.Factory.StartNew(() => FireAway(foo));फू का उपयोग करते समय सतर्क रहें , लूप में संशोधित नहीं किया जा सकता है जैसा कि यहां और यहां
एएए

22

.NET 4.5 के लिए:

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

15
Fyi, यह मुख्य रूप Task.Factory.StartNew(() => FireAway(), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);से blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx के
जिम

2
जानकर अच्छा लगा, @JimGeurts!
डेविड मर्डोक

पोस्टर के हित में, उनकी टिप्पणी में @JimGeurts से जुड़ा लेख अब 404'd (धन्यवाद, एमएस!) है। उस url में डेटास्टैम्प को देखते हुए, स्टीफन टूब का
Dan Atkinson

1
@DanAtkinson आप सही हैं। यहाँ पर मूल है। आर्काइव.ऑर्ग, बस मामले में एमएस इसे फिर से चलाता है
डेविड मर्डोक

18

को जोड़ने के लिए विल के जवाब , अगर यह एक सांत्वना अनुप्रयोग है, सिर्फ एक में फेंक AutoResetEventऔर एक 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();
    }
}

अगर यह एक कंसोल ऐप नहीं है और मेरी C # क्लास COM विज़िबल है, तो क्या आपका AutoEvent काम करेगा? क्या AutoEvent.WaitOne () अवरुद्ध है?
dan_l

5
@dan_l - कोई विचार नहीं, क्यों न एक नए प्रश्न के रूप में पूछा जाए और इस एक का संदर्भ दिया जाए।
केव

@dan_l, हाँ WaitOneहमेशा अवरुद्ध है और AutoResetEventहमेशा काम करेगा
AaA

AutoResetEvent केवल वास्तव में यहाँ काम करता है अगर वहाँ एक एकल पृष्ठभूमि धागा है। मुझे आश्चर्य है कि यदि कई धागे उपयोग में हैं तो एक बैरियर बेहतर होगा। docs.microsoft.com/en-us/dotnet/standard/threading/barrier
मार्टिन ब्राउन

@MartinBrown - यह सच नहीं है। आपके पास एक सरणी हो सकती है WaitHandle, हर एक की अपनी AutoResetEventऔर इसी के साथ ThreadPool.QueueUserWorkItem। उसके बाद बस WaitHandle.WaitAll(arrayOfWaitHandles)
केव

15

एक आसान तरीका पैरामीटरलेस लैम्ब्डा के साथ एक धागा बनाना और शुरू करना है:

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

ThreadPool.QueueUserWorkItem पर इस पद्धति का उपयोग करके आप डिबगिंग को आसान बनाने के लिए अपने नए थ्रेड का नाम दे सकते हैं। इसके अलावा, अपनी दिनचर्या में व्यापक त्रुटि से निपटने का उपयोग करना न भूलें क्योंकि डिबगर के बाहर कोई भी अखंड अपवाद आपके आवेदन को अचानक रोक देगा:

यहाँ छवि विवरण दर्ज करें


+1 जब आपको लंबे समय से चल रही या अवरुद्ध प्रक्रिया के कारण समर्पित थ्रेड की आवश्यकता होती है।
पॉल टर्नर

12

ऐसा करने का अनुशंसित तरीका जब आप Asp.Net और .net 4.5.2 का उपयोग कर रहे हैं QueueBackgroundWorkItem। यहाँ एक सहायक वर्ग है:

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
            }
        });
    }
}

उपयोग उदाहरण:

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

या async का उपयोग कर:

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

यह एज़्योर वेब साइट्स पर बहुत अच्छा काम करता है।

संदर्भ: .NET 4.5.2 में एक ASP.NET अनुप्रयोग से पृष्ठभूमि नौकरियों की अनुसूची करने के लिए QueueBackgroundWorkItem का उपयोग करना


7

StartInvoke को कॉल करना और EndInvoke को न पकड़ना एक अच्छा तरीका नहीं है। उत्तर सरल है: आपको EndInvoke को कॉल करने का कारण यह है कि क्योंकि मंगलाचरण के परिणाम (भले ही कोई रिटर्न मान न हो) को .NET द्वारा कैश किया जाना चाहिए जब तक कि EndInvoke को नहीं कहा जाता है। उदाहरण के लिए यदि आह्वान कोड एक अपवाद फेंकता है तो अपवाद आह्वान डेटा में कैश किया जाता है। जब तक आप EndInvoke कहते हैं, यह स्मृति में बनी रहती है। आपके द्वारा EndInvoke कॉल करने के बाद मेमोरी को रिलीज़ किया जा सकता है। इस विशेष मामले के लिए यह संभव है कि मेमोरी तब तक बनी रहेगी जब तक कि प्रक्रिया बंद नहीं हो जाती है क्योंकि डेटा को आह्वान कोड द्वारा आंतरिक रूप से बनाए रखा जाता है। मुझे लगता है कि GC अंततः इसे एकत्र कर सकता है लेकिन मुझे नहीं पता कि GC को कैसे पता चलेगा कि आपने डेटा को छोड़ दिया है। बस इसे पुनः प्राप्त करने के लिए वास्तव में लंबा समय ले रहा है। मुझे शक है कि यह करता है। इसलिए स्मृति रिसाव हो सकता है।

अधिक http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx पर पाया जा सकता है


3
जीसी बता सकता है कि जब कोई संदर्भ मौजूद नहीं है तो चीजों को छोड़ दिया जाता है। यदि BeginInvokeकोई रैपर ऑब्जेक्ट देता है जो एक संदर्भ रखता है, लेकिन इसके द्वारा संदर्भित नहीं होता है, तो वह ऑब्जेक्ट जो वास्तविक परिणाम डेटा रखता है, रैपर ऑब्जेक्ट अंतिम रूप देने के लिए पात्र हो जाएगा, जब सभी संदर्भों को छोड़ दिया गया था।
सुपरकैट

मैं कहता हूँ कि यह "अच्छा तरीका नहीं है"; इसका परीक्षण करें, उन त्रुटि स्थितियों के बारे में जिनसे आप चिंतित हैं, और देखें कि क्या आपको समस्याएँ हैं। यहां तक ​​कि अगर कचरा संग्रह सही नहीं है, तो यह संभवतः अधिकांश अनुप्रयोगों को ध्यान से प्रभावित नहीं करेगा। प्रति Microsoft, "यदि आप neccesary, तो प्रतिनिधि से वापसी मान प्राप्त करने के लिए EndInvoke को कॉल कर सकते हैं, लेकिन इसकी आवश्यकता नहीं है। जब तक वापसी मान पुनः प्राप्त नहीं किया जा सकता तब तक EndInvoke ब्लॉक हो जाएगा।" से: msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.90).aspx
Abacus


3

सबसे सरल .NET 2.0 और बाद का दृष्टिकोण एसिंनकॉनस प्रोग्रामिंग मॉडल का उपयोग कर रहा है (यानी। एक प्रतिनिधि पर। शुरुआत में):

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 );  
}

Task.Run()अस्तित्व में आने से पहले , यह शायद ऐसा करने का सबसे संक्षिप्त तरीका था।
बिंकी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.