2011-12-05 13 views
14

किसी भी तरह से मैं विश्वास नहीं कर सकता कि मैं उस समस्या में भाग लेने वाला पहला व्यक्ति हूं (और मैं विश्वास नहीं करना चाहता कि मैं केवल एक मूर्ख हूं जो सीधे समाधान देखने के लिए पर्याप्त नहीं है), लेकिन मेरी खोज-फू मजबूत नहीं थी पर्याप्त।समापन घटनाओं का उपयोग करते समय स्पेगेटी कोड से कैसे बचें?

मैं नियमित रूप से एक स्थिति में भाग जाता हूं, जब मुझे एक के बाद एक समय लेने वाले कदमों की आवश्यकता होती है। कार्यप्रवाह लग रहा है

तरह
var data = DataGetter.GetData(); 
var processedData = DataProcessor.Process(data); 
var userDecision = DialogService.AskUserAbout(processedData); 
// ... 

मैं प्रत्येक चरण में यूआई ब्लॉक करने के लिए नहीं करना चाहते हैं, तो हर विधि तुरंत वापसी करता है, और एक बार यह समाप्त हो गया है एक घटना को जन्म देती है। अब आनंद

DataGetter.Finished += (data) => 
    { 
     DataProcessor.Finished += (processedData) => 
     { 
      DialogService.Finished(userDecision) => 
       { 
        // .... 
       } 
       DialogService.AskUserAbout(processedData); 
      } 
     DataProcessor.Process(data); 
    }; 
DataGetter.GetData(); 

यह बहुत ज्यादा मेरे स्वाद के लिए Continuation-passing style की तरह पढ़ता है, और वहाँ इस कोड की संरचना करने के लिए एक बेहतर तरीका हो गया है में ensues, ऊपर कोड ब्लॉक mutates के बाद से। पर कैसे?

+1

बनाता है मुझे निरंतरता कार्य के बारे में सोच: http://www.blackwasp.co.uk/ContinuationTasks.aspx –

+3

यह 'await' साथ सी # 5 में बड़े पैमाने पर आसान हो जाएगा। तो हाँ, लोग हर समय इसमें भागते हैं और यह सुंदर नहीं है - वास्तव में इसे मूल भाषा में बनाने के लिए पर्याप्त है। इनपुट के लिए –

उत्तर

7

सही तरीके से आपके घटकों को एक तुल्यकालिक तरीके से डिजाइन करना होगा और पृष्ठभूमि धागे में पूरी श्रृंखला निष्पादित करना होगा।

+0

धन्यवाद! यह उपयोगकर्ता इंटरैक्शन द्वारा थोड़ा और कठिन बना दिया जाता है, जिसे आसानी से गैर-यूआई थ्रेड (कम से कम WPF में) पर नहीं किया जा सकता है। लेकिन हम निश्चित रूप से इस तरह कम स्पेगेटी स्तर प्राप्त कर सकते हैं। – Jens

+0

हम किस प्रकार के उपयोगकर्ता इनपुट के बारे में बात कर रहे हैं? –

+0

आपको उपयोगकर्ता को कार्य (एक मशीन ऑपरेटिंग) करने के लिए कहा जा सकता है और समाप्त होने पर "अगला" दबाकर, या पूछा जा सकता है कि संसाधित डेटा के साथ क्या करना है। – Jens

2

आप सब कुछ पृष्ठभूमिवर्कर में डाल सकते हैं। यदि आप सिंक्रनाइज़ेशन चलाने के लिए GetData, Process, और AskUser के तरीकों को बदलते हैं तो निम्न कोड केवल ठीक से काम करेगा।

कुछ इस तरह:

private BackgroundWorker m_worker; 

private void StartWorking() 
{ 
    if (m_worker != null) 
     throw new InvalidOperationException("The worker is already doing something"); 

    m_worker = new BackgroundWorker(); 
    m_worker.CanRaiseEvents = true; 
    m_worker.WorkerReportsProgress = true; 

    m_worker.ProgressChanged += worker_ProgressChanged; 
    m_worker.DoWork += worker_Work; 
    m_worker.RunWorkerCompleted += worker_Completed; 
} 

private void worker_Work(object sender, DoWorkEventArgs args) 
{ 
    m_worker.ReportProgress(0, "Getting the data..."); 
    var data = DataGetter.GetData(); 

    m_worker.ReportProgress(33, "Processing the data..."); 
    var processedData = DataProcessor.Process(data); 

    // if this interacts with the GUI, this should be run in the GUI thread. 
    // use InvokeRequired/BeginInvoke, or change so this question is asked 
    // in the Completed handler. it's safe to interact with the GUI there, 
    // and in the ProgressChanged handler. 
    m_worker.ReportProgress(67, "Waiting for user decision..."); 
    var userDecision = DialogService.AskUserAbout(processedData); 

    m_worker.ReportProgress(100, "Finished."); 
    args.Result = userDecision; 
} 

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs args) 
{ 
    // this gets passed down from the m_worker.ReportProgress() call 
    int percent = args.ProgressPercentage; 
    string progressMessage = (string)args.UserState; 

    // show the progress somewhere. you can interact with the GUI safely here. 
} 

private void worker_Completed(object sender, RunWorkerCompletedEventArgs args) 
{ 
    if (args.Error != null) 
    { 
     // handle the error 
    } 
    else if (args.Cancelled) 
    { 
     // handle the cancellation 
    } 
    else 
    { 
     // the work is finished! the result is in args.Result 
    } 
} 
4

Task Parallel Library इस तरह के कोड के लिए उपयोगी हो सकता है। ध्यान दें कि कार्य को चलाने के लिए TaskScheduler.FromCurrentSynchronizationContext() का उपयोग यूआई थ्रेड पर कार्य को चलाने के लिए किया जा सकता है।

Task<Data>.Factory.StartNew(() => GetData()) 
      .ContinueWith(t => Process(t.Result)) 
      .ContinueWith(t => AskUserAbout(t.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
संबंधित मुद्दे