【问题标题】:System.Threading.Tasks.TaskCanceledException: 'A task was canceled.' when Closing AppSystem.Threading.Tasks.TaskCanceledException:“任务已取消。”关闭应用程序时
【发布时间】:2018-05-16 11:55:31
【问题描述】:

我有一个线程可以改变字段

private void SongChange(object sender, RoutedEventArgs e)
    {
        SongChangeAction();
        songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
        songProgress = new Thread(SongProgressUpdate) { IsBackground = true };
        songProgress.Start();
    }

    private void SongProgressUpdate()
    {
        while (true)
        {

            Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
            Thread.Sleep(1000);
        }
    }

注意:songLengthdoubleBGMPlayerMediaElement 在后台播放音乐,workingResources.SongProgressdouble 绑定到 Progress Bar

当我使用“X”按钮或任务栏关闭程序时,程序抛出异常System.Threading.Tasks.TaskCanceledException 即使我尝试设置线程IsBackground = true,但如果我使用 Visual Studio 停止程序,它不会抛出异常。 程序抛出异常在行:

Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });

我可以通过使用 try/catch 来防止它,但我想找到更有效的方法

【问题讨论】:

  • 在您的情况下,这是预期的异常。当您关闭主窗口时 - WPF 会优雅地关闭。如果您在调度程序开始关闭时尝试执行Dispatcher.Invoke - 它会抛出您看到的异常。您可以切换到BeginInvoke,捕获该异常,或者更好地使用其他方法来实现您的目标,不需要Dispatcher.Invoke、单独的线程和while (true) 循环。
  • BGMPlayer 是一个控件。如果我使用另一个线程使用 while 循环,那么应用程序将抛出 Thread.InvalidOperation

标签: c# multithreading exception


【解决方案1】:

尝试在关闭窗口时使用CancellationToken

private readonly CancellationTokenSource _shutDown = new CancellationTokenSource();

public WindowName()
{
    this.Closed =+ (s, e) => this._shutDown.Cancel();
}

private void SongChange(object sender, RoutedEventArgs e)
{
    SongChangeAction();
    songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
    songProgress = Task.Factory.StartNew(SongProgressUpdate, this._shutDown.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

private void SongProgressUpdate()
{
    while (!this._shutDown.IsCancellationRequested)
    {
        Dispatcher.Invoke(() => { workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100; });
        Thread.Sleep(1000);
    }
}

【讨论】:

    【解决方案2】:

    尝试使用 Microsoft 的响应式框架。然后你可以这样做:

    private void SongChange(object sender, RoutedEventArgs e)
    {
        SongChangeAction();
        songLength = (int)BGMPlayer.NaturalDuration.TimeSpan.TotalSeconds;
        IDisposable subscription =
            Observable
                .Interval(TimeSpan.FromSeconds(1.0))
                .ObserveOnDispatcher()
                .Subscribe(x => workingResources.SongProgress = BGMPlayer.Position.TotalSeconds / songLength * 100);
    }
    

    您只需要确保保存对subscription 的引用并在关闭应用时调用.Dispose()

    NuGet "System.Reactive" 和 "System.Reactive.Windows.Threading",并将using System.Reactive.Linq; 添加到您的代码中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 2014-12-24
      • 2019-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多