防止GUI在WPF中挂起

时间:2016-05-07 21:00:38

标签: c# wpf asynchronous c++-cli

我对C#WPF项目相对较新,我的代码存在问题,用户界面"锁定"我正在执行一项任务。以下是相关代码:

public partial class MainWindow : Window {

    // handles to ManagedDLAContainer objects
    private ManagedDLA2DContainer dla_2d;

    public MainWindow() {
        InitializeComponent();

        // initalise aggregate containers
        dla_2d = new ManagedDLA2DContainer();
    }

    private void GenerateAggregate() {
        // generate the 2D aggregate of size given by particles_slider value
        Dispatcher.Invoke(new Action(() => { dla_2d.Generate((uint)particles_slider.Value); }));

        // TODO: add particles to "canvas" on GUI as they are generated
    }

    private void GenerateButtonClick(object sender, RoutedEventArgs e) {
        // set the coefficient of stickiness of aggregate
        // to current value of stickiness_slider
        dla_2d.SetCoeffStick(stickiness_slider.Value);

        // start asynchronous task calling GenerateAggregate method
        Task.Factory.StartNew(() => GenerateAggregate());
    }

}

此处ManagedDLA2DContainer是一些托管C++/CLI包装,用于某些原生的非托管C++代码,而stickiness_slider只是WPF界面的Slider元素;同样,particles_slider是界面的另一个Slider元素。我应该注意,以下代码也会导致GUI挂起:

public partial class MainWindow : Window {

    // lock object for multi-threading tasks
    private static readonly object locker = new object();

    // handles to ManagedDLAContainer objects
    private ManagedDLA2DContainer dla_2d;

    public MainWindow() {
        InitializeComponent();

        // initalise aggregate containers
        dla_2d = new ManagedDLA2DContainer();
    }

    private void GenerateAggregate() {
        // lock around aggregate generation
        lock (locker) {
            // generate the 2D aggregate of size given by particles_slider value
            Dispatcher.Invoke(new Action(() => { dla_2d.Generate((uint)particles_slider.Value); }));

            // TODO: add particles to "canvas" on GUI as they are generated
        }
    }

    private void GenerateButtonClick(object sender, RoutedEventArgs e) {
        // set the coefficient of stickiness of aggregate
        // to current value of stickiness_slider
        dla_2d.SetCoeffStick(stickiness_slider.Value);

        Thread agg_gen_thread = new Thread(GenerateAggregate);
        agg_gen_thread.Start();
        agg_gen_thread.Join();
    }

}

任何可以帮助我理解我在这里做错的信息都值得赞赏。

此外,如果您感到好奇,那么此项目的所有代码都可在此处获取:https://github.com/SJR276/DLAProject

1 个答案:

答案 0 :(得分:1)

您启动运行GenerateAggregate的新主题。然后立即通过Dispatcher.Invoke将所有工作返回发送到用户界面线程。所以基本上你在UI线程上运行长时间运行的任务并阻止它。

相反,您应该仅向UI线程调度需要的操作(例如,更新UI控件),而不是整个操作。

假设您的Generate函数如下所示:

void Generate() {
  MakeCpuIntensiveWork();
  UpdateUIWithResults();
}

您只需要将第二部分发送到UI线程。在您的情况下,正如我们在评论中发现的那样,唯一的UI部分是获得滑块的值。所以你可以像这样分开:

private void GenerateAggregate()
{
    uint sliderValue = 0;
    // generate the 2D aggregate of size given by particles_slider value
    Dispatcher.Invoke(() => { sliderValue = (uint)particles_slider.Value; });
    dla_2d.Generate(sliderValue);
    // TODO: add particles to "canvas" on GUI as they are generated
}