我正在尝试在运行Parallel.Foreach
时更新进度条,但在执行期间没有任何反应。仅当For循环结束时,Progressbar才会更新。如何使这段代码有效?
XAML
<StackPanel>
<Grid x:Name="LoadProgressGrid" Height="100"
Visibility="Visible">
<ProgressBar x:Name="LoadProgress"
Maximum="100"
Minimum="1" />
<TextBlock Margin="0,0,0,-5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="16"><Run Text="Import Progress"/></TextBlock>
</Grid>
<Button Height="35" Width="100" Margin="0,10" Content="Test" Click="Test_Click"/>
</StackPanel>
C#
private void Test_Click(object sender, RoutedEventArgs e)
{
decimal current=0;
List<string> lst = new List<string>();
lst.Add("Foo");
lst.Add("Foo");
lst.Add("Foo");
lst.Add("Foo");
decimal max = 100000;
var uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
Parallel.ForEach(lst, (data) =>
{
for (int i = 0; i < max; i++)
{
// Use the uiFactory above:
// Note the need for a temporary here to avoid closure issues!
current = current + 1;
uiFactory.StartNew( () => LoadProgress.Value = (double)(current/max)*100);
}
});
MessageBox.Show("Done!");
}
答案 0 :(得分:2)
正如评论中所指出的,Parallel.ForEach
在循环完成之前不会返回,这将阻止它运行的线程,并且Servy在你的答案中的评论也表明你正在访问和更改状态没有同步或对象锁定的各种线程(参见Race Conditions)。
如果您不确定在UI线程上更改状态的正确方法,那么您可以让框架执行使用IProgress<T>
捕获上下文的工作。
关于您的回答,您可以将async
关键字直接放在Test_Click
事件处理程序上,同时保留返回类型void
,但请注意不建议或建议使用空格对于任何其他async
方法,它们应返回Task
或Task<T>
,read more here on why async void
is a bad idea类型。
更重要的是,这里有一个使用async
和非阻止代码报告进度并更新进度条的代码段,我已经评论了代码更具可读性。
// Add the async keyword to our event handler
private async void Button_Click(object sender, RoutedEventArgs e)
{
//Begin our Task
Task downloadTask = DownloadFile();
// Await the task
await downloadTask;
}
private async Task DownloadFile()
{
// Capture the UI context to update our ProgressBar on the UI thread
IProgress<int> progress = new Progress<int>(i => { LoadProgress.Value = i; });
// Run our loop
for (int i = 0; i < 100; i++)
{
int localClosure = i;
// Simulate work
await Task.Delay(1000);
// Report our progress
progress.Report((int)((double)localClosure / 100 * 100));
}
}
答案 1 :(得分:0)
从这个回答:Using Task with Parallel.Foreach in .NET 4.0和Servy的评论我得到了它的工作。
private void Test_Click(object sender, RoutedEventArgs e)
{
test();
}
public async void test()
{
decimal current = 0;
List<string> lst = new List<string>();
lst.Add("Foo");
lst.Add("Foo");
lst.Add("Foo");
lst.Add("Foo");
decimal max = 10000;
//Remove await (and async from signature) if, want to see the message box rightway.
await Task.Run(() => Parallel.ForEach(lst, (data) =>
{
for (int i = 0; i < max; i++)
{
current = current + 1;
Dispatcher.Invoke(new Action(() => LoadProgress.Value = (double)(current / max) * 100));
}
}));
MessageBox.Show("Done!");
}