c# - 允许用户从按钮立即取消长时间运行的WPF操作

标签 c# wpf mvvm task

在WPF应用程序中,我们有一个按钮,用户可以单击该按钮来触发要加载到VLC Media Player中的视频列表:

<Button Content="{Binding RotatorButtonLabel}" Command="{Binding RotateVideosCommand}" />

在 View 模型MainWindowVm中,我们具有处理按钮单击的命令:
public ICommand RotateVideosCommand => new RelayCommand(RotateVideos);

private void RotateVideos()
{
    IsRotatorActive = !IsRotatorActive;
    RotatorButtonLabel = IsRotatorActive
        ? "Stop Rotator"
        : "Rotate Videos";
    _rotatorVm = new RotatorVm
    {
        ImageVms = ImagesView.Cast<ImageVm>().ToList(),
        IsRotatorActive = IsRotatorActive
    };

    // This fires off a new thread to run the rotator, otherwise the UI freezes.
    Task.Run(() => Messenger.Default.Send(rotatorVm, "LaunchRotator"));
}

请注意,在上述命令处理程序中,我们使用MVVM Light Toolkit的Messenger告诉背后的代码启动旋转器。

现在在MainWindow.xaml.cs中,我们有以下代码:
private CancellationTokenSource _cancellationTokenSource = null;
private CancellationToken _cancellationToken;

public MainWindow()
{
    InitializeComponent();
    Messenger.Default.Register<RotatorVm>(this, "LaunchRotator", LaunchRotator);

    // Other logic...
}

然后上面的LaunchRotator调用:
private void LaunchRotator(RotatorVm rotatorVm)
{
    if (_cancellationToken.IsCancellationRequested)
    {
        _cancellationTokenSource.Dispose();
    }

    if (_cancellationTokenSource == null || _cancellationToken.IsCancellationRequested)
    {
        _cancellationTokenSource = new CancellationTokenSource();
        _cancellationToken = _cancellationTokenSource.Token;
    }

    if (!rotatorVm.IsRotatorActive)
    {
        _cancellationTokenSource.Cancel();
        return;
    }

    RotateVideos(); 
}

private void RotateVideos()
{
    while (true)
    {
        if (_cancellationToken.IsCancellationRequested)
        {
            return;
        }

        // This is to simplify the code and simulate work.
        Thread.Sleep(5000);
    }
}

如果单击“停止旋转器”按钮,代码可能需要几秒钟才能到达while循环的下一个迭代并读取IsCancellationRequested。在这种情况下,如何使它立即停止?

我看过this example,但它假设任务和 Activity 都在一个类中。在这里,我有一个 View 模型和一个后台代码。谢谢你。

最佳答案

您不能(以实用的方式)也不应这样做。

如果要停止在另一个线程中工作,那么正确的方法是向该线程发出信号(就像您已经完成的那样),并允许该线程自己停止。由于您的示例工作负载是Thread.Sleep(5000),一旦被点击,该线程将无法执行任何其他操作,直到 sleep 时间结束。换句话说,您可以正确地向线程发出信号,并且如果线程正在 sleep ,它将一直存活直到 sleep 完成,然后它将再次检查该信号。

选项:

  • 在这种情况下,您可以将Token传递给模拟的工作负载
    使用Task.Wait(5000, token)并使用它代替Thread.Sleep(5000)。这样,模拟工作也可以
    取消。
  • 用于实际工作;您必须使用最佳判断来测试信号
    何时何地看起来很公平,这样您就不必长时间等待
    发出取消信号时返回的时间。请注意,让调用者等待ThreadTask本身正确结束是最好的方法
    它。这就是Thread.Abort受到如此多批评的原因,
    灰心。
  • 另一个选择是解雇。您可以在 token 源上调用Cancel(),然后继续进行操作,而无需等待TaskThread完成。您必须牢记这一点,但是这是实用的。
  • 关于c# - 允许用户从按钮立即取消长时间运行的WPF操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54354587/

    相关文章:

    c# - 使用 C++ dll 运行 C# 应用程序生成无法加载异常

    c# - 续-车牌检测

    c# - 不使用 using、foreach 或手动调用 Dispose() 时的枚举器处置

    wpf - 停止 Gridsplitter 将内容拉伸(stretch)到窗口之外

    c# - 异常在异步代码块中被忽略

    C#:从多个(USB)相机捕获静止图像

    wpf - 在 silverlight/WP7 应用程序中使用 MVVM 样式模式

    .net - 'Pinned GC handle' 背后的概念是什么?

    c# - ADO 数据服务的预测

    .net - 哪个 WPF 控制套件最适合 MVVM?