2014-02-17 57 views
3

在接下來的一段代碼:在任務中,爲什麼IProgress <T>在UI線程中正確執行,但不是Action <T>?

Task UpdateMedias<TProperty>(Expression<Func<Media, TProperty>> property, Func<Media, TProperty> func) 
{ 
    var medias = GetSelectedMedias().ToList(); 
    IProgress<double> progress = new Progress<double>(d => barQueueProgress.EditValue = d); 
    Action<Media, Expression<Func<Media, TProperty>>, Func<Media, TProperty>> action = 
     (media, expression, arg3) => UpdateMedia(media, expression, arg3); 
    Task task = Task.Run(() => 
    { 
     var i = 0; 
     foreach (var media in medias) 
     { 
      progress.Report(1.0d/medias.Count * ++i); 
      action(media, property, func); 
     } 
    }); 
    Task with = task.ContinueWith(s => 
    { 
     progress.Report(0.0d); 
     GridControl1.RefreshData(); 
    }); 
    return with; 
} 

如果我不Dispatcher.BeginInvoke包括action將與抱怨,因爲不同的線程擁有它調用線程不能訪問該對象。,而對於progress則不需要這樣做。

IProgress<T>爲什麼不需要Dispatcher.BeginInvoke

回答

8

因爲內部Progress存儲在構建時參考SynchronizationContext.Current中的內容,並在報告進度時將事件觸發到該上下文。

它是專門爲從非UI線程更新UI而設計的。如果它沒有這樣做,那麼使用它就不會有太多的理由,也不難做。

以下是我在.NET 4.5中用作Progress的一個實現。它不會和.NET的實現一樣,但它會給你一個很好的想法:

public interface IProgress<T> 
{ 
    void Report(T data); 
} 

public class Progress<T> : IProgress<T> 
{ 
    SynchronizationContext context; 
    public Progress() 
    { 
     context = SynchronizationContext.Current 
      ?? new SynchronizationContext(); 
    } 

    public Progress(Action<T> action) 
     : this() 
    { 
     ProgressReported += action; 
    } 

    public event Action<T> ProgressReported; 

    void IProgress<T>.Report(T data) 
    { 
     var action = ProgressReported; 
     if (action != null) 
     { 
      context.Post(arg => action((T)arg), data); 
     } 
    } 
} 
+1

這個答案幫助我理解了爲什麼對'progress.Report'的調用是有效的當它跟隨一個等待的'任務',配置爲'ConfigureAwait(false)'。此外,['Progress '](https://msdn.microsoft.com/en-us/library/hh193692(v = vs.110).aspx)文檔中的註釋確實確認了「 SynchronizingContext'實例,當'Progress '被構建時。 – DavidRR