gpt4 book ai didi

java - 如何使用 SwingWorker 模拟带缓冲的外围设备?

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:09:08 24 4
gpt4 key购买 nike

我将此练习用作教学工具,以帮助我深入了解一些 Java GUI 编程概念。我正在寻找的是一般理解,而不是针对某个特定问题的详细解决方案。我希望这种“正确”的编码能教会我很多关于如何处理 future 的多线程问题的知识。如果这对于这个论坛来说太笼统了,它可能属于程序员吗?

我正在模拟读卡器。它有一个 GUI,允许我们将卡片装入料斗并按开始等等,但它的主要“客户端”是 CPU,在单独的线程上运行并请求卡片。

读卡器维护一个缓冲区。如果收到卡片请求且缓冲区为空,则读卡器必须从储卡器中读取一张卡片(这需要 1/4 秒,这是 1962 年)。卡片读入缓冲区后,读卡器将缓冲区发送给CPU,并在下一次请求之前立即发起另一次缓冲区加载操作。

如果不仅缓冲区是空的,而且储卡器中也没有卡片,那么我们必须等到运算符(operator)将一副牌放入储卡器并按下开始(这始终会启动缓冲区加载操作)。

在我的实现中,卡片请求以 invokeLater() Runnables 在 EDT 上排队的形式发送到读卡器。在 myRunnable.run() 时间,缓冲区将可用(在这种情况下我们可以将其发送到 CPU 并启动另一个缓冲区加载操作),或者缓冲区将为空。如果它是空的怎么办?

两种可能性:(a) 已经有缓冲加载操作在进行中,或者 (b) 卡片槽为空(或尚未启动)。在任何一种情况下,让 EDT 等待都是 Not Acceptable 。工作(和等待)必须在后台线程上完成。

为了简单起见,我尝试生成一个 SwingWorker 来响应每个卡片请求,而不管缓冲区的状态如何。伪代码是:

SwingWorker worker = new SwingWorker<Void, Void>() {
public Void doInBackground() throws Exception {
if (buffer.isEmpty()) {
/*
* fill() takes 1/4 second (simulated by Thread.sleep)
* or possibly minutes if we need to have another
* card deck mounted by operator.
*/
buffer.fill();
}
Card card = buffer.get(); // empties buffer
/*
* Send card to CPU
*/
CPU.sendMessage(card); // <== (A) put card in msg queue
/*
* Possible race window here!!
*/
buffer.fill(); // <== (B) pre-fetch next card
return null;
}
};
worker.execute();

这产生了一些奇怪的时序效应 - 我怀疑是由于 buffer.fill() 可能发生如下情况的竞争:如果在 (A) 和 (B) 之间,CPU 接收到卡,发送了另一个请求,并代表它生成了另一个 SwingWorker 线程,那么可能有两个线程同时尝试填充缓冲区。 [删除 (B) 处的预取调用解决了这个问题。]

所以我认为为每次读取生成一个 SwingWorker 线程是错误的。卡片的缓冲和发送必须在单个线程中序列化。该线程必须尝试预取缓冲区,并且必须能够等待并在我们用完卡片并且必须等待更多卡片放入漏斗中时恢复。我怀疑 SwingWorker 具有处理此问题所需的长时间运行的后台线程,但我还没有完全做到这一点。

假设 SwingWorker 线程是要走的路,我该如何实现它,消除 EDT 上的延迟,允许线程阻塞等待料斗重新填充,并处理缓冲区填充是在另一张卡之前还是之后完成的不确定性请求到达?


编辑:我从 another thread 得到了答案并将在这里重述:

不使用 SwingWorker 线程,建议我在开始时创建一个 ExecutorService newSingleThreadExecutor() 一次,并使用 GUI 在其上排队冗长的方法execute(Runnable foo),如下(这段代码运行在EDT):

private ExecutorService executorService;
::
/*
* In constructor: create the thread
*/
executorService = Executors.newSingleThreadExecutor();
::
/*
* When EDT receives a request for a card it calls readCard(),
* which queues the work out to the *single* thread.
*/
public void readCard() throws Exception {
executorService.execute(new Runnable() {
public void run() {
if (buffer.isEmpty()) {
/*
* fill() takes 1/4 second (simulated by Thread.sleep)
* or possibly minutes if we need to have another
* card deck mounted by operator.
*/
buffer.fill();
}
Card card = buffer.get(); // empties buffer
/*
* Send card to CPU
*/
CPU.sendMessage(card); // <== (A) put card in msg queue
/*
* No race! Next request will run on same thread, after us.
*/
buffer.fill(); // <== (B) pre-fetch next card
return;
}
});
}

这与 SwingWorker 之间的主要区别在于,这确保只有一个工作线程。

最佳答案

了解 SwingWorker 可能会有所帮助使用 ExecutorService内部;为了方便起见,它添加了临时 EDT 处理机制。只要您在 EDT 上更新您的 GUI 并同步对任何共享数据的访问,后者就等同于前者。

假设您使用的是 Model–View–Controller模式,建议 here ,你的模型是一个cpu的运行。虽然它可能是不同的类,但我看不出有任何理由在不同的线程上为读卡器建模。相反,让处理器模型有一个读卡器模型来等待 java.util.Timer。线程,在计时器触发时更新模型。让更新的模型在向 EDT 发布事件的正常过程中通知 View 。让 Controller 取消并调度读卡器模型以响应查看手势。

关于java - 如何使用 SwingWorker 模拟带缓冲的外围设备?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7036509/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com