2014-08-28 8 views
6

मैं निम्नलिखित वर्ग परिभाषाएं हैं कहते हैं:सर्वश्रेष्ठ अभ्यास/इंतजार

public class Calculator 
{ 
    public CalculatorResult Calculate() 
    { 
     return LongRunningCalculation(); 
    } 

    private CalculatorResult LongRunningCalculation() 
    { 
     return new CalculatorResult(0.00); 
    } 
} 

public class ClassThatUsesACalculator 
{ 
    private readonly Calculator calculator; 

    public ClassThatUsesACalculator() 
    { 
     this.calculator = new Calculator(); 
    } 

    public void DoWork() 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      var result = calculator.Calculate(); 

      DoSomethingWithCalculationResult(result); 

      DoLightWork(); 

      OnProgressChanged(); 
     } 
    } 
} 

public partial class Form : Form 
{ 
    public Form() 
    { 
     InitializeComponent(); 
    } 

    private void Method(object sender, EventArgs e) 
    { 
     DoWork(); 
    } 

    private void DoWork() 
    { 
     var calculator = new ClassThatUsesACalculator(); 
     calculator.ProgressChanged += (s, e) => 
     { 
      // Update progressbar 
     }; 

     calculator.DoWork(); 
    } 
} 

मैं फार्म पर, काम DoWork() में किया क्या करना चाहते हैं, तो एसिंक्रोनस रूप से मैं एक विधि जोड़ सकते हैं (GetCalculationTask) जो Task.Run() का उपयोग करके एक कार्य देता है और एक बटन के लिए async eventhandler यानी (MethodOne) जोड़ें।

अगर मैं गलत हूं, तो कृपया मुझे सही करें, लेकिन मुझे लगता है कि यह एकमात्र विकल्प होगा जब ClassThatUsesACalculator और Calculator कक्षाएं उस लाइब्रेरी में रहती हैं जो मेरे पास नहीं है।

private Task GetCalculationTask(IProgress<CalculatorProgress> progress) 
{ 
    var calculator = new ClassThatUsesACalculator(); 
    calculator.ProgressChanged += (s, e) => 
    { 
     progress.Report(new CalculatorProgress(0)); 
    }; 

    return Task.Run(() => 
    { 
     calculator.DoWork(); 
    }); 
} 

private async void MethodOne(object sender, EventArgs e) 
{ 
    IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress> (UpdateProgressBar); 

    await GetCalculationTask(progress); 
} 

यदि मेरे पास लाइब्रेरी है तो मुझे लगता है कि दो और विकल्प हैं, जिनमें से एक पहले की तरह बहुत अधिक है। शायद मेरी समझ की कमी के कारण।

ClassThatUsesACalculator पर एक विधि बनाएं जो DoWork() विधि को समाहित करता है और फिर उसे फॉर्म पर एक असीमित विधि से कॉल करता है।

या,

  1. एक Task.Run() साथ Calculator वर्ग पर LongRunningCalculation() समाहित।

    public Task<CalculatorResult> CalculateAsync() 
    { 
        return Task.Run(() => 
        { 
         return LongRunningCalculation(); 
        }); 
    } 
    
  2. ClassThatUsesACalculator पर कॉल कि नव निर्मित विधि इंतजार कर रहा है एक async विधि बनाएँ।

    public async Task DoWorkAsync() 
    { 
        for (int i = 0; i < 10; i++) 
        { 
         var result = await calculator.CalculateAsync(); 
    
         DoSomethingWithCalculationResult(result); 
    
         DoLightWork(); 
    
         OnProgressChanged(); 
        } 
    } 
    
  3. अब फार्म पर एक अतुल्यकालिक विधि (MethodThree)

    private async void MethodThree(object sender, EventArgs e) 
    { 
        IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar); 
    
        var calculator = new ClassThatUsesACalculator(); 
        calculator.ProgressChanged += (s, args) => 
        { 
         progress.Report(new CalculatorProgress(0)); 
        }; 
    
        await calculator.DoWorkAsync(); 
    } 
    

बनाएं, मेरी राय में आखिरी विकल्प सबसे अच्छा होगा के रूप में मैं और अधिक नियंत्रण रहेगा। लेकिन हो सकता है कि मैं रास्ता बंद कर दूं और इस पर किसी की राय या पॉइंटर्स चाहूंगा क्योंकि मुझे केवल एसिंक का उपभोग करने के बारे में स्पष्टीकरण मिल सकता है, लेकिन वास्तव में दूसरों को उपभोग करने के तरीकों का निर्माण कैसे नहीं करना चाहिए।

+5

आपका प्रश्न वास्तव में लंबा और अधिकतर शब्द है- यदि आप इसे जानना चाहते हैं तो संक्षेप में समझने के लिए इसे संकुचित कर सकते हैं। वैसे, त्वरित सामान के लिए, 'async' वास्तव में आवश्यक नहीं है। 'async' I/O कार्यों के लिए एक फ़ाइल, डेटाबेस या वेब सेवा तक पहुंचने के लिए बहुत अच्छा है। लेकिन गैर-आई/ओ कोड के त्वरित बिट्स चलाने के लिए ओवरहेड वास्तव में प्रदर्शन को कम कर सकता है। – mason

उत्तर

11

एक सामान्य नियम के रूप में, कॉल स्टैक जितना संभव हो सके Task.Run उपयोग को दबाएं।

आप से बचने के लिए एक अतुल्यकालिक हस्ताक्षर के साथ एक विधि है जो एक पुन: प्रयोज्य घटक में Task.Run का उपयोग करके कार्यान्वित किया गया है। यह एक झूठ एपीआई है। मेरे पास blog post on the subject है जो अधिक विस्तार से जाता है।

यदि आप प्रश्न में कक्षाओं को नियंत्रित करते हैं, तो मैं प्रगति अद्यतनों के लिए ईवेंट के बजाय IProgress<T> का उपयोग करने की अनुशंसा करता हूं। IProgress<T> काम करता है तुल्यकालिक कोड के साथ ठीक के साथ ही अतुल्यकालिक:

public void DoWork(IProgress<CalculatorProgress> progress = null) 
{ 
    for (int i = 0; i < 10; i++) 
    { 
    var result = calculator.Calculate(); 

    DoSomethingWithCalculationResult(result); 

    DoLightWork(); 

    if (progress != null) 
     progress.Report(new CalculatorProgress(...)); 
    } 
} 
फिर

इसे का उपयोग काफी सीधा है:

private async void MethodTwo(object sender, EventArgs e) 
{ 
    IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar); 

    var calculator = new ClassThatUsesACalculator(); 

    await Task.Run(() => calculator.DoWork(progress)); 
} 

कि घटक इसकी आवश्यकता है कि में Task.Run उपयोग रहता है - यूआई परत - और व्यापार तर्क से बाहर।

+1

इसे पर्वत की चोटी से गाया जाना चाहिए। – rmirabelle

संबंधित मुद्दे