【问题标题】:Handle long-running EDT tasks (f.i. TreeModel searching)处理长时间运行的 EDT 任务(f.i. TreeModel 搜索)
【发布时间】:2012-03-11 19:16:31
【问题描述】:

Trigger 是最近的re-detected SwingX issue:支持深度 - 即在折叠节点下,而不是仅可见节点,这是当前行为 - 节点搜索。

“Nichts leichter als das”与我目前对 SwingWorker 的所有接触:在后台线程中遍历 TreeModel 并更新进程中的 ui,如下面的粗略 sn-p 所示。 Fest 的 EDT 检查器很高兴,但它只检查重绘(这在 EDT 上很好地发生在这里)

只有...严格来说,后台线程必须是 EDT,因为它正在访问(通过读取)模型。所以,问题是:

  • 如何正确实现搜索线程?
  • 或者我们能否承受这种风险(当然,有大量记录)

特殊情况解决方案的一种可能性是使用第二个(克隆或“相同”制造的)模型进行搜索,然后在“真实”模型中找到相应的匹配项。这与一般搜索支持并不能很好地配合,因为它无法了解任何特定模型的任何信息,即使它想要也无法创建克隆。另外,它必须应用所有视图排序/过滤(将来)...

// a crude worker (match hard-coded and directly coupled to the ui)
public static class SearchWorker extends SwingWorker<Void, File> {

    private Enumeration enumer;
    private JXList list;
    private JXTree tree;

    public SearchWorker(Enumeration enumer, JXList list, JXTree tree) {
        this.enumer = enumer;
        this.list = list;
        this.tree = tree;
    }

    @Override
    protected Void doInBackground() throws Exception {
        int count = 0;
        while (enumer.hasMoreElements()) {
            count++;
            File file = (File) enumer.nextElement();
            if (match(file)) {
                publish(file);
            }
            if (count > 100){
                count = 0;
                Thread.sleep(50);
            }    
        }
        return null;
    }


    @Override
    protected void process(List<File> chunks) {
        for (File file : chunks) {
            ((DefaultListModel) list.getModel()).addElement(file);
            TreePath path = createPathToRoot(file);
            tree.addSelectionPath(path);
            tree.scrollPathToVisible(path);
        }
    }

    private TreePath createPathToRoot(File file) {
        boolean result = false;
        List<File> path = new LinkedList<File>();
        while(!result && file != null) {
            result = file.equals(tree.getModel().getRoot());
            path.add(0, file);
            file = file.getParentFile();
        }
        return new TreePath(path.toArray());
    }

    private boolean match(File file) {
        return file.getName().startsWith("c");
    }

}

// its usage in terms of SwingX test support
public void interactiveDeepSearch() {
    final FileSystemModel files = new FileSystemModel(new File("."));
    final JXTree tree = new JXTree(files);
    tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, StringValues.FILE_NAME));
    final JXList list = new JXList(new DefaultListModel());
    list.setCellRenderer(new DefaultListRenderer(StringValues.FILE_NAME));
    list.setVisibleRowCount(20);
    JXFrame frame = wrapWithScrollingInFrame(tree, "search files");
    frame.add(new JScrollPane(list), BorderLayout.SOUTH);
    Action traverse = new AbstractAction("worker") {

        @Override
        public void actionPerformed(ActionEvent e) {
            setEnabled(false);
            Enumeration fileEnum = new PreorderModelEnumeration(files);
            SwingWorker worker = new SearchWorker(fileEnum, list, tree);
            PropertyChangeListener l = new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                        //T.imeOut("search end ");
                        setEnabled(true);
                        ((SwingWorker) evt.getSource()).removePropertyChangeListener(this);
                    }
                }
            };
            worker.addPropertyChangeListener(l);
            // T.imeOn("starting search ... ");
            worker.execute();
        }

    };
    addAction(frame, traverse);
    show(frame)
 } 

仅供参考:交叉发布到 OTN's Swing forumSwingLabs forum - 将尝试在最后发布所有输入的摘要(如果有的话 :-)

附录

在一天结束的时候,事实证明我问了错误的问题(或在错误的上下文中提出了正确的问题 ;-):“问题”是由 假定的解决方案引起的,真正的要解决的任务是支持分层搜索算法(现在 AbstractSearchable 严重偏向于线性搜索)。

一旦解决了这个问题,下一个问题可能是框架可以做多少来支持具体的分层搜索。鉴于 TreeModels 的各种自定义实现,这很可能仅适用于最简单的实现。

在此处和其他论坛的讨论中提出的一些想法。在具体的上下文中,首先测量遍历是否很慢:大多数内存模型的遍历速度非常快,除了使用基本支持之外什么都不需要做。

仅当遍历是瓶颈时(如在 SwingX 的 FileSystemModel 实现中),才需要额外的工作:

  • 在真正不可变且不可修改的 TreeModel 中,我们可能会在 SwingWorker 的后台线程中进行只读访问
  • 在延迟加载/删除场景中违反了不可修改的前提条件
  • 可能存在支持模型的自然自定义数据结构,它实际上与实际模型“分离”,允许同步到支持模型(在遍历模型和视图模型中)
  • 将实际搜索传回数据库
  • 在给定 TreeModel 之上使用包装器,以保证访问 EDT 上的底层模型
  • “假”背景搜索:实际上是在 EDT 上足够小的块中进行(例如在计时器中),这样用户就不会注意到任何延迟

无论慢搜索的技术选项是什么,都需要解决相同的可用性问题:如何将延迟呈现给最终用户?这是一个完全不同的故事,可能更依赖于上下文/需求:-)

【问题讨论】:

  • 如果有两个或多个 SwingWorker 实例会发生什么情况,此代码不会取消/终止先前/现有的 SwingWorkers 实例(如果存在)
  • 在读取过程中模型可以“离线”吗?也就是阻止对模型的所有修改,遍历并从EDT中读取出来,读取完成后再重新连接模型?
  • @mKorbel 好点 :-) 蹩脚的借口:这只是一个简单的例子,生产代码(希望)会更干净
  • @HovercraftFullOfEels 嗯......不是真的:可搜索的是每棵树的实例,模型可以在多个视图之间共享。但很好的收获 - 还要记住另一件事:-)

标签: java swing concurrency jtree swingx


【解决方案1】:

SwingWorkerTreeModel 都应该同步对公共底层DataModel 的访问。使共享的数据量(有效地)不可变可以最大限度地减少开销。由于这高度依赖于应用程序,我不确定该视图可以为搜索不可见节点提供多少支持。

【讨论】:

  • 免责声明:我几乎没有使用 SwingX 的经验。
  • +1 好点(如果我理解正确的话,有点像我天真的“使模型相同”)。并同意,这对于框架来说很困难。顺便说一句:没什么特别的 SwingX :-)
  • @mKorbel 的好奇回答让我想起了 FileBrowser 的早期版本@ 这种树/工人争用。经过反思,解决方案通常是将问题推回多用户关系型数据库。
  • 绝对是慢速遍历的两个选项 :-)
  • @kleopatra:您的附录中的分析非常出色。顺便说一句,使用嵌入式数据库,例如H2 可以减轻开销。
猜你喜欢
  • 1970-01-01
  • 2012-04-10
  • 2021-09-11
  • 2011-01-19
  • 1970-01-01
  • 2013-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多