BackgroundWorker + WPF -> 窗口无响应

3

-进度条始终为0%

-窗口被冻结(在DoWork运行时)

-如果在开启状态下执行System.Threading.Thread.Sleep(1),则可以完美地工作

问题出在哪里?

private void btnNext_Click(object sender, RoutedEventArgs e)
{
  this._worker = new BackgroundWorker();
  this._worker.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                long current = 1;
                long max = generalMaxSzam();


                for (int i = 1; i <= 30; i++)
                {
                    for (int j = i+1; j <= 30; j++)
                    {
                        for (int c = j+1; c <= 30; c++)
                        {
                            for (int h = c+1; h <= 30; h++)
                            {
                                for (int d = h+1; d <= 30; d++)
                                {
                                    int percent = Convert.ToInt32(((decimal)current / (decimal)max) * 100);
                                    this._worker.ReportProgress(percent);
                                    current++;
                                    //System.Threading.Thread.Sleep(1); - it works well
                                }
                            }
                        }
                    }
                }
            };

            this._worker.WorkerReportsProgress = true;

 this._worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
                this.Close();
            };

 this._worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
            {                              
                this.statusPG.Value = args.ProgressPercentage;             
            };

 this._worker.RunWorkerAsync();
}

<Window x:Class="SzerencsejatekProgram.Create"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Létrehozás" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="500" Width="700">    
    <DockPanel>    
        <Button DockPanel.Dock="Right"  Name="btnNext" Width="80" Click="btnNext_Click">Tovább</Button>
        <StatusBar DockPanel.Dock="Bottom">
            <StatusBar.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="auto"/>
                            <ColumnDefinition Width="auto"/>
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </StatusBar.ItemsPanel>
            <StatusBarItem Grid.Column="1">
                <TextBlock Name="statusText"></TextBlock>
            </StatusBarItem>
            <StatusBarItem Grid.Column="2">
                <ProgressBar Name="statusPG" Width="80" Height="18" IsEnabled="False" />
            </StatusBarItem>
            <StatusBarItem Grid.Column="3">
                <Button Name="statusB" IsCancel="True" IsEnabled="False">Cancel</Button>
            </StatusBarItem>
        </StatusBar>
    </DockPanel>
</Window>
3个回答

2

您的代码运行了一个非常紧密的循环,循环中心调用了ReportProgress()。

这意味着您的消息队列被请求执行进度更新所淹没。

如果在Bgw线程中加入延迟(Thread.Sleep(100)),您会看到响应性得到改善。

一个更实际的解决方案是将报告移出到外部循环。在您的情况下:

for (int i = 1; i <= 30; i++)
{
    int percent = (i * 100) / 30;
    _worker.ReportProgress(percent);
    for(int j = 0; ....)
        ....
}

如果只有一个循环,请加入延迟:'if ((counter % 100) == 0) ...`

你的目标是用户,调用Reportprogress的次数应在10到100之间。

基于10(00)%或类似的报告...目前有一个同事遇到了相同的问题,因为他正在报告250000条记录的进度。 - cyberzed
thread.Sleep(100) -> 不是一个解决方案。我认为减少报告会起作用。 - Valetudox
@cyber,@Value:当然 Sleep() 不是真正的程序所需。但这个内部循环只是增加一个变量。我稍微扩展了我的答案。 - H H
嗯,它会获取30^5次的进度更新,所以我仍然更喜欢报告较少的进度。进度更新的常见目标是向用户显示系统没有死机,因为他们并不真的关心您是否有10个记录还是1000万个记录(除非您故意告诉他们您将处理多少项)。 - cyberzed
啊,没看到你在“i”循环而不是“d”循环中:D - cyberzed

0
 if (current++ % onePercent == 0)
                    {
                         int percent = Convert.ToInt32(((decimal)current / (decimal)max) * 100);
                         this._worker.ReportProgress(percent, new WorkerUserState { current = current, max = max });                        
                    }

这个运行良好。


自从您降低了频率:) - Amsakanna

0

你为ProgressChanged事件编写的匿名方法将在UI线程上运行。由于你频繁地报告进度,它将被调度程序排队并阻塞UI。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接