Parallel.ForEach x of x
So I'm working in c# 4.0 WPF application and using parallel foreach loops to export data to a database using a database repository I created. I have got the export working with parallel foreach using a progress bar but would like to be able to give more in depth details of the progress like exporting item 5 of 25. The problem i've encountered is a obvious one, because it is running in parallel the counter does not work, i.e. the total will say something like
exporting 0 of 25
exporting 0 of 25
...
exporting 5 of 25
exporting 5 of 25
Can anyone give any guidence of how to get the behaviour working within开发者_开发问答 a parallel loop like this:
int runningTotal = 0;
Parallel.ForEach(source, x =>
{
Repository.Commit(x);
runningTotal++;
progressReporter.ReportProgress(() =>
{
//Progress bar update
this.progressFile.Value++;
this.lblProgress.Text = String
.Format("Exporting Source {0} of {1}", runningTotal, source.Count)
});
});
Hope this shows what I'm hoping to achieve.
Thanks
As you're using the same data from multiple thread, you should protect it's access with a locking mechanism. It can be done easily by using the lock
construct.
However, it might be a little too overkill for your need. As you're only incrementing a value, a simple System.Threading.Interlocked.Increment
would work. On msdn.
int runningTotal = 0;
Parallel.ForEach(source, x =>
{
Repository.Commit(x);
Interlocked.Increment(ref runningTotal);
progressReporter.ReportProgress(() =>
{
//Progress bar update
Interlocked.Increment(ref this.progressFile.Value);
this.lblProgress.Text = String
.Format("Exporting Source {0} of {1}", runningTotal, source.Count)
});
});
EDIT: I've made a sample in WPF.
Window.xaml :
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0" Click="Button_Click">Click me</Button>
<TextBlock Grid.Row="1">
<TextBlock.Text>
<MultiBinding StringFormat="Progress: {0}/{1}">
<Binding Path="ProgressValue" RelativeSource="{RelativeSource AncestorType=Window, Mode=FindAncestor}" />
<Binding Path="ProgressMax" RelativeSource="{RelativeSource AncestorType=Window, Mode=FindAncestor}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
Window.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
int progressValue;
public int ProgressValue
{
get { return (this.progressValue); }
set { this.progressValue = value; this.raisePropertyChanged("ProgressValue"); }
}
public int ProgressMax
{
get { return (100); }
}
public event PropertyChangedEventHandler PropertyChanged;
void raisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
int counter = 0;
Parallel.ForEach(Enumerable.Range(1, 100), i =>
{
Interlocked.Increment(ref counter);
this.ProgressValue = counter;
Thread.Sleep(25);
});
});
}
}
I'm simply using bindings to display a string indicating the work progress. Everything is stored in the Window which implements INotifyPropertyChanged
to force bindings to refresh and display the updated value. It can easily be moved to it's own class which will implement INotifyPropertyChanged
for a cleaner code.
精彩评论