可取消在.NET中排序?

时间:2009-11-21 20:21:20

标签: c# .net user-interface sorting

我在VirtualMode中使用ListView来显示非常多的行,数百万行。 存储在通用列表中的行数据。

现在我希望实现一个排序功能,它将按一些Comparer对List进行排序。

问题是,目前,平均单一排序大约需要30秒,在此期间,用户无法对ListView执行任何操作,必须等到结束。

并非每个用户都会接受等待那么多时间,如果可能的话,大多数用户会取消排序,并且我想允许该取消功能。不幸的是,内置的List.Sort无法取消,也无法取消Array.Sort。

现在排序发生在单独的线程上,所以我可以使用Thread.Abort,但它可能会导致List损坏,对我来说是不可接受的。

除了我自己重新实现整个Sort算法外,我能做些什么吗?

感谢。

5 个答案:

答案 0 :(得分:5)

复制列表,在线程中对副本进行排序,然后替换原始列表(如果排序完成而不会中断)。

但是如果可能的话,我会考虑Martinho的建议 - 在应用程序中开始使用数百万行感觉不对。数据库可以在数据到达之前对数据进行过滤和排序。

答案 1 :(得分:3)

我有类似的问题。击败框架Array.Sort并不容易,并且根本不建议使用Thread.Abort。 我决定排序操作不可取消,但我想到了这个解决方案(没有测试)

实现您自己的比较器,能够访问具有CancelRequested bool字段的IAsyncResult对象。在进行任何比较之前,检查一下bool,true会在你的bground线程中引发异常。没有线程中止,例如任何锁都可以正确释放。您的初始物品是安全的。但是您仍然需要构建一个数组,无论是ref还是索引原始项目的键,因为在比较时抛出可能会导致数组被破坏。我想情况并非如此,但对此没有保证。

希望这有帮助

答案 2 :(得分:2)

有几种方法可以做到这一点。我可能会编写一个使用标准BeginXXX / EndXXX异步模式的类和一个特殊的CancelXXX方法。我遗漏了很多代码,但我认为这里有足够的代码来说明问题。不幸的是,使用这种方法,您必须编写自己的排序算法。

public class Sorter
{
  public IAsyncResult BeginSort(IList<T> values, AsyncCallback complete)
  {
    MyAsyncResult asyncResult = new MyAsyncResult();
    Thread t = new Thread(() =>
      {
        // Implement your sorting algorithm here.
        // Periodically check asyncResult.Cancel at safe points.
        asyncResult.Complete();
        if (complete != null)
        {
          complete(asyncResult);
        }
      });
    t.Start();
    return asyncResult;
  }

  public void EndSort(IAsyncResult asyncResult)
  {
    MyAsyncResult target = asyncResult as MyAsyncResult;
    if (target == null)
    {
      throw new ArgumentException();
    }
    // Add code here to extract any additional information from the IAsyncResult that 
    // you might want to return to the client. Perhaps this method will be empty.
  }

  public void CancelSort(IAsyncResult asyncResult)
  {
    MyAsyncResult target = asyncResult as MyAsyncResult;
    if (target == null)
    {
      throw new ArgumentException();
    }
    target.Cancel = true;
  }

  private class MyAsyncResult : IAsyncResult
  {
    private volatile bool m_Cancel = false;

    public bool Cancel
    {
      get { return m_Cancel; }
      set { m_Cancel = value; }
    }

    public void Complete()
    {
      // Add code here to mark this IAsyncResult as complete.
    }
  }
}

答案 3 :(得分:1)

完全取决于所讨论的环境 - 一种方法是让用户取消等待排序(在单独的线程上运行),但是你秘密地继续在后台排序列表,然后告诉他们什么时候完成一个微妙的通知。

答案 4 :(得分:0)

如果您需要取消排序,则必须创建一个单独的线程。您可以通过重新复制原始列表并对副本进行排序来防止损坏原始列表。如果用户没有取消排序,只需交换原始列表wist sort one。