跟踪Parallel Foreach线程

use*_*446 5 c# parallel-processing wpf multithreading

我目前正在编写一个简单的WPF文件复制应用程序,它可以并行复制文件.到目前为止它很棒!它做我想做的一切.操作的内容在以下代码块中:

Parallel.ForEach(Directory.GetFiles(dir).ToList(), file =>
{
    _destDetail.CurrOp = string.Format("Copying file: {0}", Path.GetFileName(file));
    File.Copy(file, file.Replace(_destDetail.Source, _destDetail.Dest), true);
    if (_destDetail.Progress < _destDetail.MaxProgress)
        _destDetail.Progress++;
});
Run Code Online (Sandbox Code Playgroud)

我也可以实现ParallelOptions并将最大线程数限制为4,但我想知道是否有办法准确地跟踪每个线程在这种情况下会做什么?

例如,假设我的UI的一部分专用于复制操作的当前"状态".我希望有4行Grid,每行有一个特定的线程和当前正在复制的文件.

我知道我可以使用Interlocked以操纵是外部变量的Parallel循环,但我怎么会跟踪线程特定变量从内部的中Parallel环,并使用这些变量来保持UI最新哪个线程正在哪个文件?

Sco*_*ain 4

不是直接跟踪线程,而是将 UI 绑定到ObserveableCollection<ProgressDetail>每个代表进度的线程,然后在循环中让它在启动时将一个项目添加到集合中,然后在结束时将其从集合中删除。

您必须注意的一件事是线程安全,ObseveableCollection它不是线程安全的,因此您必须仅以线程安全的方式与其交互,最简单的方法是ProgressDetail在 UI 线程上进行所有对象的添加和删除。这还有一个额外的好处,即在创建对象时捕获 UI 线程的 SynchronizationContext Progress

public ObserveableCollection<ProgressDetail> ProgressCollection {get; private set;}

public void CopyFiles(string dir)
{

    var dispatcher = Application.Current.Dispatcher;
    Parallel.ForEach(Directory.GetFiles(dir).ToList(), file =>
    {
        ProgressDetail progressDetail = null;
        dispatcher.Invoke(() => 
        {
           // We make the `Progress` object on the UI thread so it can capture the  
           // SynchronizationContext during its construction.
           progressDetail = new ProgressDetail(file);
           ProgressCollection.Add(progressDetail);
        }

        XCopy.Copy(file, file.Replace(_destDetail.Source, _destDetail.Dest), 
                   true, false, progressDetail.ProgressReporter);

        dispatcher.Invoke(() => ProgressCollection.Remove(progressDetail);
    });

}

public sealed class ProgressDetail : INotifyPropertyChanged
{
    private double _progressPercentage;

    public ProgressDetail(string fileName)
    {
        FileName = fileName;
        ProgressReporter = new Progress<double>(OnProgressReported);
    }

    public string FileName { get; private set; }
    public IProgress<double> ProgressReporter { get; private set; }
    public double ProgressPercentage
    {
        get { return _progressPercentage; }
        private set
        {
            if (value.Equals(_progressPercentage)) return;
            _progressPercentage = value;
            OnPropertyChanged();
        }
    }

    private void OnProgressReported(double progress)
    {
        ProgressPercentage = progress;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var temp = PropertyChanged;
        if(temp != null)
            temp(this, new PropertyChangedEventArgs(propertyName));
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅此答案XCopy以获取将随进度复制的示例类。我假设 的签名Copy已更改为

public static void Copy(string source, string destination, bool overwrite, bool nobuffering, IProgress<double> handler)
Run Code Online (Sandbox Code Playgroud)

但我将实际的更改留给读者作为练习。

更新:我已经更新了上面的代码示例以公开ProgressPercentage可以绑定并引发适当事件的公共属性。我还将Progress事件的监听转移到了班级内部ProgressDetail