2013-08-06 4 views
5

공통 포맷으로 각각의 데이터로 변환하고 상기 서비스 층의 각 메소드는 새로운 시작 등UI 스레드를 차단하지 않고 여러 작업을 수행 한 후 어떻게 계속합니까? 제 MVVM 애플리케이션 내보기 모델은, 3 개 개의 다른 서비스 메소드 호출에서

속성 통지/관찰 모음을 이용한 UI를 업데이트 Task을 입력하고 Task을 뷰 모델에 반환합니다. 다음은 내 서비스 방법 중 하나의 예입니다.

public class ResourceService 
{ 
internal static Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback) 
{ 
    var t = Task.Factory.StartNew(() => 
    { 
     //... get resources from somewhere 
     return resources; 
    }); 

    t.ContinueWith(task => 
    { 
     if (task.IsFaulted) 
     { 
      errorCallback(task.Exception); 
      return; 
     } 
     completedCallback(task.Result); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 

    return t; 
} 
} 

여기

private ObservableCollection<DataItem> Data = new ObservableCollection<DataItem>(); 

public ICollectionView DataView 
{ 
    get { return _dataView; } 
    set 
    { 
     if (_dataView != value) 
     { 
      _dataView = value; 
      RaisePropertyChange(() => DataView); 
     } 
    } 
} 

private void LoadData() 
{ 
    SetBusy("Loading..."); 

    Data.Clear(); 

    Task[] tasks = new Task[3] 
    { 
     LoadTools(), 
     LoadResources(), 
     LoadPersonel() 
    }; 

    Task.WaitAll(tasks); 

    DataView = CollectionViewSource.GetDefaultView(Data); 
    DataView.Filter = FilterTimelineData; 

    IsBusy = false; 
} 

private Task LoadResources() 
{ 
    return ResourceService.LoadResources(resources => 
    { 
     foreach(var r in resources) 
     { 
      var d = convertResource(r); 
      Data.Add(d); 
     } 
    }, 
    error => 
    { 
     // do some error handling 
    }); 
} 

이 거의 작품 ... 호출 코드와 뷰 모델의 다른 관련 부분이다하지만 작은 문제가 몇 가지 있습니다.

번호 1 : 처음에 SetBusy을 호출 할 때 어떤 작업을 시작하기 전에 WaitAll을 호출하기 전에 IsBusy 속성을 true로 설정합니다. 이 UI를 업데이트하고 BusyIndicator 컨트롤을 표시해야하지만 작동하지 않습니다. 또한 간단한 문자열 속성을 추가하고 바인딩하려고 시도했지만 업데이트되지 않았습니다. IsBusy 기능은 기본 클래스의 일부이며 하나 이상의 작업이 실행되지 않는 다른보기 모델에서 작동하므로 XAML의 속성 알림 또는 데이터 바인딩에 문제가 있다고 생각하지 않습니다.

전체 데이터 바인딩은 전체 메서드가 완료된 후 업데이트 된 것처럼 보입니다. "처음 예외"또는 UI 스레드가 어떻게 든 WaitAll 호출하기 전에 차단되는 믿을 선도하는 출력 창 바인딩 오류가 표시되지 않습니다.

2 번 : 서비스 방법에서 잘못된 작업을 반환하는 것 같습니다. 보기 모델이 모든 결과를 콜백의 모든 서비스 메소드에서 변환 한 후에 실행하려면 WaitAll 이후의 모든 것을 원합니다. 그러나 서비스 메서드에서 연속 작업을 반환하면 연속이 호출되지 않으며 WaitAll은 영원히 기다립니다. 이상한 일은 ICollectionView에 바인딩 된 UI 컨트롤이 실제로 모든 것을 올바르게 표시한다는 것입니다. 데이터가 관찰 가능한 컬렉션이고 CollectionViewSource가 컬렉션 변경 이벤트를 인식하고 있기 때문에 이것을 가정했습니다.

답변

9

TaskFactory.ContinueWhenAll을 사용하면 입력 작업이 모두 완료 될 때 실행되는 연속을 빌드 할 수 있습니다. 당신이 사용하는 경우

Task[] tasks = new Task[3] 
{ 
    LoadTools(), 
    LoadResources(), 
    LoadPersonel() 
}; 

Task.Factory.ContinueWhenAll(tasks, t => 
{ 
    DataView = CollectionViewSource.GetDefaultView(Data); 
    DataView.Filter = FilterTimelineData; 

    IsBusy = false; 
}, CancellationToken.None, TaskContinuationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext()); 

참고이 간단하게하는 것이 C# 5의 await/async 구문 : 나는 서비스 방식에서 계속 작업을 반환하는 경우


는 그러나 계속이

private async void LoadData() 
{ 
    SetBusy("Loading..."); 

    Data.Clear(); 

    Task[] tasks = new Task[3] 
    { 
     LoadTools(), 
     LoadResources(), 
     LoadPersonel() 
    }; 

    await Task.WhenAll(tasks); 

    DataView = CollectionViewSource.GetDefaultView(Data); 
    DataView.Filter = FilterTimelineData; 

    IsBusy = false; 
} 
라는 결코 극복 그리고 WaitAll은 영원히 기다린다.

문제는 계속 작업에 UI 스레드가 필요하며 WaitAll 호출에서 UI 스레드를 차단하고 있다는 것입니다. 이로 인해 해결되지 않는 교착 상태가 발생합니다.

위의 내용을 수정하면 완료로 기다릴 필요가있는 작업으로 Continuation을 반환하려고합니다. 그러나 TaskFactory.ContinueWhenAll을 사용하면 UI 스레드를 해제하여 해당 연속을 처리 할 수 ​​있습니다.이 같이 당신은 다른 방법을 쓸 수 있습니다 5. C#을 단순화됩니다 다른 것입니다

참고 :

internal static async Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback) 
{ 
    try 
    { 
    await Task.Run(() => 
    { 
     //... get resources from somewhere 
     return resources; 
    }); 
    } 
    catch (Exception e) 
    { 
    errorCallback(task.Exception); 
    } 

    completedCallback(task.Result); 
} 

말했다되는 즉, 그것은 제공하는 대신 Task<T>를 반환하는 방법을 쓰기 위해 일반적으로 좋습니다 콜백은 사용법의 양쪽 끝을 단순화합니다.

+0

WPF 명령과 Prism DelegateCommand로 "그냥 작동하는지"확실하지 않기 때문에 async/await을 사용하지 말았습니다. Task.ContinueWhenAll이 존재하지 않는 것 같습니다. 일부 TPL 확장 라이브러리를 참조해야합니까? – BenCr

+0

@BenCr 죄송합니다 .- 작업의 틀입니다 .ContinueWhenAll. 일반적으로 대기/비동기 작업은 WPF의 작업 계속보다 더 잘 작동합니다. 주요 차이점은 예외를 처리하기 위해 미친 농구를 뛰어 넘을 필요가 없다는 것입니다. –

+0

감사 리드, 이것은 훨씬 더 잘 작동하는 것 같습니다. WaitAll이 호출되기 전에 누군가가 차단에 대한 설명을 제공 할 수 있기를 바랍니다.하지만이 문제는 확실히 1 및 2 문제를 해결했습니다. 다음 반복에서 비동기/대기중인 작업을 수행하고 어떻게 진행되는지 봅니다. – BenCr