WPF : Progress Bar update in Parallel.Foreach

夙愿已清 提交于 2020-01-26 04:21:05

问题


I am trying to update the progress bar while running a Parallel.Foreach, but during execution nothing happens. Progressbar gets updated only when the For loop end. How can I make this code work?

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!");
         }   

回答1:


As pointed out in the comments, Parallel.ForEach does not return until the loop has completed which will block the thread it runs on, and the comments by Servy in your answer also say that you are accessing and changing state from various threads without synchronization or object locking (see Race Conditions).

If you are unsure about the correct way to change state on the UI Thread then you can leave the framework do the work of capturing the context with IProgress<T>.

Regarding your answer, you can put the async keyword directly on the Test_Click event handler while keeping the return type void, but please bare in mind that void is not recommended or suggested for any other async methods and they should return a type of Task or Task<T>, read more here on why async void is a bad idea.

More to the point, here's a snippet making use of async and non-blocking code to report progress and update the progress bar, I have commented the code to be more readable.

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



回答2:


From this answer: Using Task with Parallel.Foreach in .NET 4.0 and Servy's comment I got it working.

 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!");
        }


来源:https://stackoverflow.com/questions/41025517/wpf-progress-bar-update-in-parallel-foreach

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!