如果发生错误,IO线程警报GUI线程

时间:2010-08-19 20:11:34

标签: java multithreading user-interface client-server notifications

我有一个客户端/服务器问题,我正试图找出最佳解决方案。

如果客户端因为任何原因而从服务器断开连接,我想让输入输出线程有一种方法来警告gui线程出错的地方,从而让gui线程打印错误并优雅地处理它(可能会退回到登录gui)。创建初始gui线程后,客户端可以更改为任意数量的guis,具体取决于他正在做什么,所以我想我需要一种方法来动态地查看当前正在运行的gui。

到目前为止,我一直在想这样做:

1)创建一个创建并显示每个gui的对象。所以不要调用invokeLater ...... SomeGui.CreateAndShoGui()......我们会让这个对象负责这样做,即GuiObject.showSomeGui();

2)让每个gui实现一个接口,这将确保有一个方法,当被调用时,将在我们失去与服务器的连接时正常关闭这个gui。

3)有一个监视IO线程和gui对象的线程。如果IO线程出现问题,IO线程将关闭并通知监视线程我们已丢失与服务器的连接。监视线程然后可以警告任何打开的guis(来自gui对象)我们已经失去连接并且它需要关闭。

我刚开始考虑这个问题,到目前为止,这是我提出的最佳解决方案。这似乎是一个合理的解决方案,不会给代码增加太多的复杂性吗?或者,任何人都可以推荐一个对于阅读代码的人来说更容易理解的解决方案吗?

由于

编辑: 我正在使用的另一个选项是在IO线程上有一个对象,它也会在打开时传递给每个新的gui。此对象将当前打开的guis引用返回给io线程,以便io线程可以在出现问题时提醒它。我倾向于反对这个解决方案,因为如果你有一个专门用于实现这个工作的对象(比如上面的解决方案),它似乎会更容易阅读,而不是将一些模糊的对象传递给每个gui。

1 个答案:

答案 0 :(得分:2)

让我简单介绍一下你的每一个想法:

1)糟糕的主意 - 您通过单个对象将整个应用程序绑定在一起。这使得可维护性变得困难,并且是模块化的对立面。

2)这是去恕我直言的方式。由于似乎每个gui在失败场景中都具有独特的逻辑,因此理所当然地认为最能理解该做什么的对象将是gui对象本身。

这个想法的另一个版本是为每个gui创建一个适配器来放置这个失败逻辑。优点是您的应用程序框架和gui之间的依赖性较小。缺点是这是一个额外的复杂层。如果您的gui已经非常适合您的应用程序,那么我会选择接口方法。如果你想在另一个应用程序中重用你的guis,那么适配器方式可以帮助实现这一点。

3)这很好地补充了#2。所以让我直截了当 - 你将有3个线程:IO线程,监视器线程和UI线程。我不知道你是否需要监控线程。根据您的说法,IO线程将能够自己检测连接问题(可能是因为捕获了某种形式的IOException)。当发现连接问题时,IO线程不忙,因为它只是很快关闭自己,所以它也可能只是有责任通知guis有问题。 guis应该在UI线程上调用它们的接口方法,这样IO线程只是调用一堆invokeLater()调用(或者对SWT的asyncExec()调用)然后IO线程可以自行关闭。

4)(您的编辑)您基本上是在描述访客模式。我不认为这是一个很好的解决方案,因为调用是从IO线程到gui而不是相反。在这种情况下,我不确定如何传递访问者对象会有所帮助。

最后一个想法。如果您使接口通用(不是特定于gui),则可以将此模式应用于其他资源。例如,您可能希望在断开连接时刷新用户凭据(因为您曾谈到再次访问登录屏幕)。这不是真正的逻辑,不应该从gui课程中完成。

修改我会使用事件模型。假设你创建了一个这样的界面:

public interface ConnectionFailureListener {
    void handleConnectionFailure(); // Add an event object if you need it
}

然后,您可以在某个对象中使用注册方法(可能是IO线程的Runnable或其他方便您的对象)。这些方法非常标准:

public void addConnectionFailureListener(ConnectionFailureListener l) {}
public void removeConnectionFailureListener(ConnectionFailureListener l) {}

当您在屏幕上显示gui时,您会将其添加到注册对象中,当您关闭gui时,您会将其从注册对象中删除。您可以根据需要添加其他类型的对象 - 例如,当您登录时,可以为凭证系统添加侦听器,并在处理注销时再次将其删除。

这样,当您遇到故障时,只需循环浏览当前已注册的侦听器,并且侦听器就可以执行此操作。