2

我的 Swing 应用程序中有 2 个进程要执行,一个用于填充列表,一个用于对列表中的每个元素进行操作。我刚刚将 2 个进程移动到 Swingworker 线程中,以在执行任务时停止 GUI 锁定,并且因为我需要对多个列表执行这组操作,所以在第一个并发性上并不是一个坏主意地方。然而,当我刚跑

fillList.execute();
doStuffToList.execute();

doStuffToList 线程在空列表上运行(duh ...)。如何告诉第二个进程等到第一个进程完成?我想我可以将第二个进程嵌套在第一个进程的末尾,但我不知道,这似乎是一种不好的做法。

4

4 回答 4

1

我想这样的事情会做到吗?

boolean listIsFull=false;
class FillListWorker extends SwingWorker<Foo,Bar>
{
    ...
    protected void done()
    {
        synchronized (listYouveBeenFilling)
        {
            listIsFull=true;
            listYouveBeenFilling.notifyAll();
        }
    }
    ...
}

class DoStuffToListListWorker extends SwingWorker<Foo,Bar>
{
    ...
    protected Foo doInBackground()
    {
        synchronized (listYouveBeenFilling)
        {
            while (!listIsFull)
            {
                try
                {
                    listYouveBeenFilling.wait();
                }
                catch (InterruptedException ie)
                {
                    // Don't worry, we'll just wait again
                }
            }
        }
    }
    ...
}
于 2009-05-27T14:16:40.260 回答
1

如何告诉第二个进程等到第一个进程完成?我想我可以将第二个进程嵌套在第一个进程的末尾,但我不知道,这似乎是一种不好的做法。

您是否考虑过使用可调用对象和期货?它们听起来很适合这类事情(让 doStuffToList 在 Future.get() 而不是实际列表上工作,所以它会在调用 get 时准备好),除了整个 swingworker 业务......(将此视为建议而不是答案)

于 2009-05-27T15:10:09.793 回答
0

我们有这样的东西:

private SwingWorkerExecutor swingWorkerExecutor;

//...

protected void runChain(List<SwingWorker<Void>> chainWorkers,
                        final SwingWorkerExecutor.RunAfter<Void> runAfter,
                        final SwingWorkerExecutor.RunOnError runOnError)
{
    final List<SwingWorker<Void>> remainingWorkers =
        chainWorkers.subList(1, chainWorkers.size());
    SwingWorkerExecutor.RunAfter<Void> chainRunAfter;
    if (chainWorkers.size() > 1)
    {
        chainRunAfter = new SwingWorkerExecutor.RunAfter<Void>()
        {
            @Override
            public void run(Void value)
            {
                runChain(remainingWorkers, runAfter, runOnError);
            }
        };
    }
    else
    {
        chainRunAfter = runAfter;
    }

    currentWorker = chainWorkers.get(0);

    swingWorkerExecutor.execute(currentWorker, chainRunAfter, runOnError);
}

这很简单,IMO,因为在我们的例子中 SwingWorkerExecutor 实际上包含了所有难以理解的东西:

public class DefaultSwingWorkerExecutor implements SwingWorkerExecutor
{
    @Override
    public <T> void execute(SwingWorker<T, ?> worker, RunAfter<T> after,
                            RunOnError onError)
    {
        worker.addPropertyChangeListener(
            new RunAfterHandler<T>(worker, after, onError));
        worker.execute();
    }

    private static class RunAfterHandler<T> implements PropertyChangeListener
    {
        private final SwingWorker<T, ?> worker;
        private final RunAfter<T> after;
        private final RunAfter<Throwable> onError;

        protected RunAfterHandler(SwingWorker<T, ?> worker, RunAfter<T> after,
                                  RunOnError onError)
        {
            this.worker = worker;
            this.after = after;
            this.onError = onError;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt)
        {
            if ("state".equals(evt.getPropertyName()) &&
                evt.getNewValue() == SwingWorker.StateValue.DONE)
            {
                if (worker.isCancelled())
                {
                    return;
                }

                try
                {
                    after.run(worker.get());
                }
                catch (InterruptedException e)
                {
                    Thread.currentThread().interrupt();
                }
                catch (ExecutionException e)
                {
                    onError.run(e);
                }
            }
        }
    }
}

有一些缺少的接口应该很直接地编写而不在这里看到它们。

我们真正的部署 SwingWorkerExecutor 使用注入的 ExecutorService 而不是默认的执行(这减少了单个应用程序所需的线程池数量。)但我们引入 SwingWorkerExecutor 的真正原因是它简化和标准化了 SwingWorker 成功和错误的处理条件并且还允许替换单元测试的逻辑(我相信你知道,如果它们是单线程的,那么它们会简单得多。)正如你所看到的,每个 SwingWorker 通常都需要一堆样板文件在 done() 内部,所以我们不这样做,而是将 done() 工作移到回调中。

附带的好处是像在一个链中运行多个 Swing 工作者这样的事情变得非常容易实现。

于 2010-04-12T05:09:06.767 回答
-1

要按顺序执行两个过程,传统上您只需调用一个方法,然后调用另一个方法(!)。

fillList();
doStuffToList();

或者可能是这样的:

doStuffToList(fillList());

如果您一次处理一个,您可能需要两个线程BlockingQueue之间有一个。您可能会通过多个 do-stuff 线程走得更远。

就 AWT 事件调度线程 (EDT) 而言,它只是在没有阻塞的情况下分离出一个动作,稍后会收到通知。

于 2009-05-27T15:04:15.750 回答