防止 GUI 在 WPF 中挂起

Preventing GUI hangs in WPF

我是 C# WPF 项目的新手,我的代码存在问题,用户界面 "locks up" 而我 运行 是一项任务。这是相关代码:

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++ 代码的托管 C++/CLI 包装器,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

您开始了您 运行 GenerateAggregate 的新线程。然后立即通过 Dispatcher.Invoke 将所有工作 back 分派给用户界面线程。所以基本上你在 UI 线程上 运行ning long 运行ning 任务并且它阻塞了。

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

假设您的生成函数如下所示:

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
}