2011-12-05 2 views
14

어쨌든 나는 그 문제에 부딪 치는 첫 번째 사람이라고 믿을 수 없다. (그리고 나는 해결책을 직접 볼 수 없을만큼 바보 같은 사람이라고 믿고 싶지 않다.)하지만 내 검색 부는 강하지 않았다. 충분히.완료 이벤트를 사용할 때 스파게티 코드를 피하는 방법은 무엇입니까?

시간이 많이 걸리는 단계를 차례로 수행해야 할 때 정기적으로 상황에 처하게됩니다. 워크 플로우는 다음과 같습니다.

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

각 단계에서 UI를 차단하고 싶지 않으므로 모든 메소드가 즉시 리턴하고 완료되면 이벤트를 발생시킵니다. 이제 환희는

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

이 너무 많이 내 취향 Continuation-passing style처럼 읽고,이 코드를 구성하는 더 나은 방법이있을가에 위의 코드 블록 변이 때문에, 계속된다. 그러나 어떻게?

+1

나를 연속 작업의 생각하게한다 : http://www.blackwasp.co.uk/ContinuationTasks.aspx –

+3

이것은'await'와 C# 5에서 대규모 쉬울 것이다. 그렇습니다. 사람들은 항상이 문제에 뛰어 들고 있습니다. 실제로 언어를 핵심 언어로 만들기에 충분하지 않습니다. –

답변

7

올바른 방법은 구성 요소를 동기식으로 디자인하고 백그라운드 스레드에서 전체 체인을 실행하는 것입니다.

+0

입력 해 주셔서 감사합니다! 이것은 사용자 상호 작용에 의해 조금 더 어려워집니다. UI가 아닌 스레드 (WPF 이상)에서는 쉽게 수행 할 수 없습니다. 그러나 우리는 확실히 스파게티 레벨을 적게받을 수 있습니다. – Jens

+0

어떤 종류의 사용자 입력에 대해 이야기하고 있습니까? –

+0

작업이 완료되면 작업을 수행하고 (컴퓨터 작동 중) "다음"을 누르거나 처리 된 데이터를 어떻게 처리할지 묻는 메시지가 나타날 수 있습니다. – Jens

2

모든 것을 BackgroundWorker에 넣을 수 있습니다. 다음 코드는 GetData, Process 및 AskUserAbout 메서드를 동 기적으로 실행하도록 변경하면 제대로 작동합니다. 이 같은

뭔가 :

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()는 UI 스레드에서 작업을 실행하는 데 사용할 수 있습니다.

Task<Data>.Factory.StartNew(() => GetData()) 
      .ContinueWith(t => Process(t.Result)) 
      .ContinueWith(t => AskUserAbout(t.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
관련 문제