1

我有大约 40000 个可能需要重新粉刷的对象。他们中的大多数都没有出现在屏幕上,所以我似乎可以通过同时进行检查来节省很多工作。但是,我的 CPU 使用率从未超过 15%,所以它似乎仍然只使用一个内核。我是否正确实现了线程?如果是这样,为什么我的所有核心都没有被使用?有没有更好的方法可以利用我所有的核心?

public void paintComponent(Graphics g)
{
    super.paintComponent(g);

    if (game.movables.size() > 10000)
    {
        final int size = game.drawables.size();
        final Graphics gg = g;
        Thread[] threads = new Thread[8];
        for (int j = 0; j < 8; ++j)
        {
            final int n = j;
            threads[j] = new Thread(new Runnable()
            {
                public void run()
                {
                    Drawable drawMe;
                    int start = (size / 8) * n;
                    int end = (size / 8) * (n + 1);
                    if (n == 8) end = game.drawables.size(); // incase size
                                                             // % 8 != 0
                    for (int i = start; i < end; ++i)
                    {
                        drawMe = game.drawables.get(i);
                        if (drawMe.isOnScreen())
                        {
                            synchronized (gg)
                            {
                                drawMe.draw(gg);
                            }
                        }
                    }
                }
            });
            threads[j].start();
        }

        try
        {
            for (int j = 0; j < 8; ++j)
                threads[j].join();
        }
        catch (InterruptedException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    else
    {
        for (Drawable drawMe : game.drawables)
        {
            if (drawMe.isOnScreen())
            {
                drawMe.draw(g);
            }
        }
    }
}
4

4 回答 4

3

正如已经指出的那样,synchronized (gg)它有效地序列化了所有绘图,因此由于线程创建和其他开销,您可能比单线程代码慢。

然而,我写这篇文章的主要原因是 Swing,这大概是,不是线程安全的。所以这个程序的行为不仅可能是坏的,而且是未定义的。

像这样的线程错误在一些带有一些 java 运行时参数和一些图形驱动程序的机器上会出现。到过那里。做到了。不好。

JOGL将让您直接访问 GPU,这是加速渲染的最可靠方法。

于 2012-07-18T04:33:05.190 回答
1

要做到这一点,您可以首先将每个 drawMe 放入一个(正确同步的)列表中,然后在连接完成后将它们实际绘制在一个循环中。您无法加快绘图速度(尽管如果您已经完成了 99% 的 drawMe,您将大大减少所需的时间),但如果isOnScreen()有点复杂,您将从核心中获得一些真正的工作。

ConcurrentLinkedQueue将使您无需同步添加到列表中。

下一步可能是使用阻塞队列而不是列表,因此绘制代码可以与可见性检查并行运行。随着八次检查的进行,他们应该远远领先于图纸。(但我认为所有阻塞队列要么需要同步,要么自己同步。我会跳过这个并坚持使用 CLQ 和第一个解决方案。更简单,可能更快。)

而且(正如 Gene 指出的),与 Swing 相关的所有内容都从 EventQueue 开始。把它留在那里,否则生活会变得奇怪。只有您自己的代码(引用 UI)应该在您的线程中运行。

于 2012-07-18T20:00:12.690 回答
0

由于您已经没有绘制任何屏幕外的对象,因此通过执行上述操作您可能获得的收益非常少。

我什至会说你让情况变得更糟,但是引入synchronize哪个很慢,并且还引入了导致上下文切换的线程,这很昂贵。

为了提高性能,您或许应该考虑使用不同的绘图库,例如 Java2D 绘图库,它是 JDK 的一部分:http: //java.sun.com/products/java-media/2D/index.jsp

于 2012-07-18T04:32:31.797 回答
-1

我不确定 java 将如何处理这个问题,但是如果你像你正在使用的那样跨范围引用某些东西final int n(因为当循环停止时它超出范围),其他语言会爆炸并死掉。考虑使其成为可运行对象的字段。此外,您在进行所有实际工作的同时也在图形对象上进行同步。很可能您并没有从中获得任何真正的性能提升。您可能会受益于显式检查对象是否并行显示在屏幕上,这是一个只读操作,将屏幕上的对象添加到其他类型的集合或集合中,然后按顺序渲染。

于 2012-07-18T04:09:35.117 回答