【问题标题】:C# Synchronized Method call kills current processingC# 同步方法调用终止当前处理
【发布时间】:2017-05-08 17:11:13
【问题描述】:

我有一个ObservableCollection<string> 绑定到 WPF 视图。有一种方法可以刷新集合的内容。此方法在后台线程中处理,需要几秒钟。为避免错误,调用的方法是同步的。

即使之前的处理尚未完成,也可能会调用该方法。在这种情况下,我想停止当前的处理并开始新的处理。

private ObservableCollection<string> col = new ObservableCollection<string>();

private async void Refresh()
{
    var ui = TaskScheduler.FromCurrentSynchronizationContext();
    await Task.Factory.StartNew(() =>
        {
            return GetNewObjects();
        }).ContinueWith(t =>
        {
            col = t.Result;
        }, ui); 
}

[MethodImpl(MethodImplOptions.Synchronized)]
private ObservableCollection<string> GetNewObjects() 
{
    // processing
}

我的想法是在 Refresh() 中保存相应 Task 的引用,并检查每个调用,如果已经有正在运行的任务。这是停止任务的正确(和安全)方式吗?

【问题讨论】:

  • 您可能会尝试CancellationToken。顺便说一句,为什么不Task.Run()?你还在使用 .NET 4.0 吗?
  • 老实说,不。我对.net 一点也不陌生。我正在寻找一种刷新视图的好方法,而不冻结 ui 线程。这是我的第一个工作方式。为什么使用 Task.Run()?有什么好处?
  • 我认为这是不可能的。 UI 线程上的延续是一个致命的细节,总是会导致死锁。不是唯一的问题,GetNewObjects() 必须使用来自 UI 的 some 类型的数据来产生不同的结果。我们看不到它,但唯一有意义的事情。您不能允许该数据在执行时更改。
  • 每次单击另一个 ObservableCollection 中的复选框时,都会调用 GetNewObjects()。每个选中的复选框都将成为 GetNewObjects() 方法的输入。实际上,“[MethodImpl(MethodImplOptions.Synchronized)]”会导致顺序处理。但这就是问题所在。当方法被调用 2 次时,有趣的结果是最后一次调用。每次调用后,之前的结果都不再相关。
  • Task.Run() 是在 .NET 4.5+ 中启动任务的首选方法,请阅读 this answer 了解详细信息。

标签: c# wpf multithreading


【解决方案1】:

您应该一直遵循async/await 路径。

要取消任务,请使用CancellationTokenSource 及其CancellationToken

private ObservableCollection<string> col = new ObservableCollection<string>();

private CancellationTokenSource _refreshCancellation;

private async void Refresh()
{
    // cancel the last Refresh action
    if ( _refreshCancellation != null )
    {
        _refreshCancellation.Cancel();
    }

    _refreshCancellation = new CancellationTokenSource();
    var token = _refreshCancellation.Token;

    try
    {
        var newObjects = await GetNewObjectsAsync( token );
        col = new ObservableCollection<string>( newObjects );
    }
    catch ( OperationCanceledException )
    {
    }

}

private async Task<ICollection<string>> GetNewObjectsAsync( CancellationToken cancellationToken )
{
    for ( int i = 0; i < 5; i++ )
    {
        await Task.Delay( 100 ).ConfigureAwait( false );
        // check if we had to cancel, but only where it is safe to cancel
        cancellationToken.ThrowIfCancellationRequested();
    }
    return new List<string> { "a", "b", "c", };
}

【讨论】:

  • 感谢您的建议。我的后台任务是 SQL 查询而不是循环。所以我需要终止这个 SQL 操作。这完全可以通过 CancellationToken 实现吗?在阅读了这里的帖子和这些令牌的功能后,我遇到了这个新问题。
  • 如果您有新问题,如果找不到任何解决方案,您应该提出新问题。这就是 SO 的工作原理
  • 这是一个关于取消 SQL 查询的 SO stackoverflow.com/questions/24738417/…
  • 感谢四位您的帮助.. 你是对的。我遇到了一个新问题。我会尝试解决这个问题。否则我会问一个新问题。不过,您的回答解决了这个问题。
猜你喜欢
  • 2019-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多