多线程GUI update()方法

时间:2019-05-12 16:47:38

标签: multithreading user-interface model-view-controller design-patterns observers

我是多线程的开端。我最近开始写类似多线程观察器的东西。我需要澄清一下。

假设我正在与Subject合作,并且我正在更改其状态。然后必须通知观察者(例如GUI窗口小部件),以便他们可以执行update()方法。

我有一个问题:我如何处理许多观察员执行的getValue()?如果它只是某个变量的吸气剂,我是否必须在新线程中运行它?需要锁定吗?

或者也许有一种方法可以将这些新值发送到GUI线程,然后让其中的小部件访问这些值。再说一次,这可能是一个循环,还是我必须为每个小部件创建另一个线程才能获得这些值?

1 个答案:

答案 0 :(得分:0)

这是一个很难的主题。以下几件事将为您提供指导和帮助。

  • 拥抱最终的一致性。当一个对象在一个线程上更新时,其他对象将收到更改通知并最终最终更新到正确状态。不要试图使所有内容始终保持同步。不要指望一切都保持最新状态。设计系统以处理这些情况。查看this视频。

  • 使用不变性,尤其是对于集合。从多个线程读取和写入集合可能会导致灾难。不要这样使用不可变的集合或使用快照。基本上,将从多个线程调用的一个对象将返回集合状态的快照。当收到更改通知时,阅读器(在您的情况下为GUI)将请求新状态的快照并进行相应更新。

  • 设计丰富的Models。不要使用只有settersgetters的{​​{3}},而要让其他人来操纵它们。让 模型 保护其数据并提供其状态查询。不要从对象的属性返回可变对象。

  • 传递带有更改通知的描述更改的数据。这样,读取器(GUI)可以仅从更改数据同步其状态,而不必读取目标对象。

  • 划分责任。让GUI知道它是单线程的,并从后台接收到通知。不要在您的 模型 中添加知识,这些知识将在后台线程上进行更新,并且不要知道它是从GUI调用的,并承担将更改请求发送到的责任。一个特定的线程。 模型 不应该在意这样的东西。它引发通知,并让订户按照需要的方式处理它们。让GUI知道更改通知将在后台接收,因此可以将其传输到UI线程。

选中AnemicModels。它描述了执行多线程的不同方法。

您没有显示任何代码或指定的语言,因此,我将为您提供一个使用类似Java / C#语言的伪代码示例。

public class FolderIcon {

    private Icon mIcon;

    public Icon getIcon() { return mIcon; }

    public FolderIcon(Icon icon) {
        mIcon = icon;
    }
}

public class FolderGUIElement : Observer {

    private Folder mFolder;
    private string mFolderPath;

    public FolderGUIElement(Folder folder) {

        mFolder = folder;
        mFolderPath = mFolder.getPath();

        folder.addChangeListener(this);
    }

    public void onSubjectChanged(change c) {

        if(c instanceof PathChange) {
            dispatchOnGuiThread(() => {
                handlePathChange((PathChange)change);
            });
        }
    }

    handlePathChange(PathChange change) {
        mFolderPath = change.NewPath;
    }
}

public class Folder : Subject {

    private string mPath;
    private FolderIcon mIcon;

    public string getPath() { return mPath; }

    public FolderIcon getIcon() { return mIcon; }

    public void changePath(string newPath) {
        mPath = patnewPath;
        notifyChanged(new PathChange(newPath));
    }

    public void changeIcon(FolderIcon newIcon) {
        mIcon = newIcon;
        notifyChanged(new IconChange(newIcon));
    }
}

请注意示例中的几件事。

  1. 我们正在使用 文件夹 中的不可变对象。这意味着GUI元素无法获取PathFolderIcon的值并对其进行更改,从而影响 文件夹 。更改图标时,我们将创建一个全新的 FolderIcon 对象,而不是修改旧的对象。 文件夹 本身是可变的,但是它使用不可变的对象作为其属性。如果需要,可以使用完全不变的对象。混合方法效果很好。

  2. 当我们收到变更通知时,我们会从 PathChange 中读取 NewPath 。这样,我们不必再次调用 文件夹

  3. 我们拥有changePathchangeIcon方法,而不是setPathsetIcon。这样可以更好地捕获我们的操作意图,从而为我们提供模型行为,而不仅仅是袋getterssetters

如果您还没有读过this video,我强烈推荐您。这与多线程无关,而与如何设计丰富的模型有关。每个开发人员都应该在我的书籍清单中阅读。 DDD中的概念为Domain Driven Design。它是不可变的,并提供了一种实现模型的好方法,在多线程系统中尤其有用。