2010-12-10 5 views
2

아래 코드를 사용하면 최종 ContinueWith에서 최종 UI 업데이트가 수행되지 않습니다. 나는 이것이 내가 끝내주는 대기 (Wait) 때문이라고 생각한다.작업 흐름 시퀀스가 ​​잘못되었습니다.

내가 그 이유는 Wait이 없기 때문에 메서드가 백그라운드에서 생성 된 완성되기 전에 IDataProvider를 반환하기 때문입니다.

누군가 내가이 권리를 얻을 수 있도록 도와 줄 수 있습니까?

건배,
Berryl

 private IDataProvider _buildSQLiteProvider()   
    { 

     IDataProvider resultingDataProvider = null; 
     ISession session = null; 

     var watch = Stopwatch.StartNew(); 

     var uiContext = TaskScheduler.FromCurrentSynchronizationContext(); 

     // get the data 
     var buildProvider = Task.Factory 
      .StartNew(
       () => 
        { 
         // code to build it 
        }); 

     // show some progress if we haven't finished 
     buildProvider.ContinueWith(
      taskResult => 
      { 
       // show we are making progress; 
      }, 
      CancellationToken.None, TaskContinuationOptions.None, uiContext); 

     // we have data: reflect completed status in ui 
     buildProvider.ContinueWith(
      dataProvider => 
      { 
       // show we are finished; 
      }, 
      CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiContext); 

     try { 
      buildProvider.Wait(); 
     } 

     catch (AggregateException ae) 
     { 
      foreach (var e in ae.InnerExceptions) 
       Console.WriteLine(e.Message); 
     } 
     Console.WriteLine("Exception handled. Let's move on."); 

     CurrentSessionContext.Bind(session); 

     return resultingDataProvider; 
    } 

====

나는 문제가 UI 스레드 이야기를 가지고 있지 않다 명확합니다. 첫 번째 업데이트는 계속 진행됩니다. 내가 겪고있는 문제는 마지막 업데이트와 데이터 제공 업체의 반환시기입니다.

나는이 게시물의 잡음 수준을 줄이고 작업 시퀀싱에 초점을 맞추기 위해 코드를 주석 처리했다.

====

확인, 작업은 나에게 TPL을 보증 같은이 코드는 보이지 않는

 private void _showSQLiteProjecPicker()   
    { 

     var watch = Stopwatch.StartNew(); 
     var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     ISession session = null; 

     // get the data 
     var buildProvider = Task.Factory.StartNew(
       () => 
       { 
        var setProgress = Task.Factory.StartNew(
         () => 
         { 
          IsBusy = true; 
          Status = string.Format("Fetching data..."); 
         }, 
         CancellationToken.None, TaskCreationOptions.None, uiScheduler); 

        var provider = new SQLiteDataProvider(); 
        session = SQLiteDataProvider.Session; 
        return provider; 
       }); 


     buildProvider.ContinueWith(
      buildTask => 
       { 
        if(buildTask.Exception != null) { 
         Console.WriteLine(buildTask.Exception); 
        } 
        else { 
         Check.RequireNotNull(buildTask.Result); 
         Check.RequireNotNull(session); 

         _updateUiTaskIsComplete(watch); 

         CurrentSessionContext.Bind(session); 
         var provider = buildTask.Result; 
         var dao = provider.GetActivitySubjectDao(); 
         var vm = new ProjectPickerViewModel(dao); 
         _showPicker(vm); 
        } 
       }, 
      CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiScheduler); 
    } 

답변

3

UPDATE
코드 아래에. 대신 BackgroundWorker를 대신 사용하는 것 같습니다!

어느 쪽이든, 별도의 스레드에서 UI를 업데이트 할 수 없으므로 업데이트가 발생하지 않을 가능성이 있습니다. UI 스레드에서 업데이트를 실행해야합니다. 당신은이에 대한 Dispatcher를 (http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms는 WPF와 윈폼 모두에 대한 정보를 포함)를 사용한다

업데이트 :

그래서 분명히 코드 중 일부가 누락되어 개정 된 답변을 얻었습니다. 우선, Nicholas는 정확합니다 .ContinueWith는 새로운 작업을 반환합니다 (http://msdn.microsoft.com/en-us/library/dd270696.aspx). 그래서 그 대신

var result = Task.Factory.StartNew(...); 
result.ContinueWith(...); 

의 당신은 아마 새 작업을 만든 다음 모든 ContinueWith() 통화를하고 작업에 할당 한 다음 작업에 .Start()를 호출합니다. 예 :

var task = new Task(...).ContinueWith(...); 
task.Start(); 

그러나 디자인에 결함이 있습니다 (내가 본 것처럼)! 이 코드를 비동기 적으로 실행하려고 시도하고 있습니다. 이유는 스레드와 TPL을 사용하는 이유입니다. 그러나이 작업이 완료 될 때까지 UI 스레드를 차단하는 UI 스레드에서 buildProvider.Wait();을 호출합니다. UI 스레드가 차단되어있는 동안 ContinueWith()의 UI를 다시 칠하는 문제 외에도 UI 스레드 (주요 아니요 아니요)를 차단할 때 멀티 스레딩에는 이점이 없습니다. 아마도 당신은 Bind()을 ContinueWith 또는 뭔가 안에 붙이면 Wait()에 전화 할 필요가 없으며 UI 스레드를 차단할 수 있습니다.

My $ 0.02는 쿼리를 수행하는 데 오랜 시간이 걸릴 것으로 예상되는 경우 2 개의 스레드 (또는 TPL의 작업)입니다. 하나는 쿼리를 수행하고 다른 하나는 상태 간격으로 UI를 업데이트하는 것입니다. 오랜 시간이 걸릴 것으로 예상하지 않으면 단일 스레드 (작업)에서 쿼리를 수행 한 다음 UI가 완료 될 때 업데이트하는 것이 좋습니다. 아마 BackgroundWorker을 통해이 작업을 수행 할 것입니다. TPL은 많은 작업과 연속성 등을 관리하기 위해 만들어졌지만 이러한 종류의 작업에는 과도한 것처럼 보입니다. 훨씬 적은 코드로 BackgroundWorker를 사용하여 작업을 수행 할 수 있다고 생각합니다. 그러나 TPL을 사용하기를 원한다는 말을 들었지만 실제로 다시 실행되도록 약간 재 작업해야합니다!

PS는 - 당신은 아마 내가 좀 헷갈리는을 해요 catch

+0

BGW는 자체적으로 문제를 제기하지만이 시나리오에서는 괜찮을 것이라고 동의합니다. 나는 TPL이 더 쉽고 유연해질 것이라 생각한다. 그리고이 질문은 TPL 사용에 관한 것이지 진척 상황을보고하는 방법이 아니다. 더 중요한 것은 코드를 살펴보면 WPF (좋은 점)에 의존하지 않고 UI 컨텍스트를 캡처하고 ContinueWith *가 "UI를 업데이트합니다."라고 말합니다. 건배 – Berryl

+0

@Statis. 나는 그것을 원한다. 네, 끝에서의 기다림은 패배자였습니다. 첫 번째 연속과 마찬가지로 진행을 업데이트합니다. 마지막 연속은 개념적으로 정확했습니다. 첫 번째 연속 대신에 새로운 작업이 필요합니다. 당신과 Nicholas가 제안 했음)하지만 원래 태스크 내부에 중첩되어 있어야하며 UI 스레드가 있어야합니다 .TPL을 배우고 비동기 프로그래밍에 대한 접근 방식을 표준화하기를 원할뿐 아니라 결국에는 다른 태스크를 피킹하고 싶습니다. BGW에 고통 스러울 것입니다. 도와 주셔서 감사합니다. – Berryl

2

내부의 Console.WriteLine("Exception handled. Let's move on.");를 넣어 의미하지만, 마지막으로 나는 그것을 혼란 발견 된 TPL을 사용했다. ContinueWith()은 새 Task 인스턴스를 반환합니다. 따라서 두 번째 ContinueWith() 결과를 var continuedTask = builderProvider.ContinueWith(...) 등의 새 변수에 할당 한 다음 buildProvider.ContinueWith() 대신 continuedTask.ContinueWith()을 참조하도록 마지막 변수를 변경해야합니다. 마지막으로 TaskWait()이 표시됩니다.

희망 하시겠습니까?

관련 문제