在一个Task中,为什么IProgress <t>在UI线程中正确执行而不是Action <t>?</t> </t>

时间:2014-02-17 17:06:44

标签: c# multithreading task dispatcher

在下面的代码中:

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;
}

如果我不将actionDispatcher.BeginInvoke包含在内,它会抱怨调用线程无法访问此对象,因为另一个线程拥有它。而{{1}没有必要这样做。

progress如何运作而不需要IProgress<T>

1 个答案:

答案 0 :(得分:8)

因为内部Progress存储对SynchronizationContext.Current构建时的内容的引用,并在报告进度时将事件触发到该上下文。

它专门用于从非UI线程更新UI。如果它没有这样做,就没有那么多理由使用它,也不是很难做到。

这是我用作Progress pre .NET 4.5的实现。它与.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);
        }
    }
}