我正在研究一种简单的进化算法。
我有一个网格地图,每个细胞都是空白,植物或草食动物。 植物和食草动物是生物(植物不是,无论如何),所以它们都有能量和颜色。生物每隔几毫秒(当计时器触发时)就会发生任何事情。植物不断补充能量,食草动物失去能量。如果植物完全充电,它会以随机方向(北,南,东或西)观察,如果该方向的相邻细胞是空白,植物将再生成该细胞。草食动物通过食用植物恢复能量。当草食动物轮到做东西时,它会看着一个随机相邻的细胞。如果它是空的,食草动物就会移动到那里。如果它是一种植物,草食动物会尝试吃它然后移动到那里。食草动物的颜色越接近植物',草食动物成功的机会就越大。如果食草动物达到0能量,它就会死亡。如果食草动物达到最大能量,它也会繁殖。后代的颜色总是与父母的颜色略有不同。
这是一张图片:
这是从内部吃掉的很多植物。
过了一会儿,系统正常化了:
大片会消失,并且随时会出现几种颜色。当然,这是预期的。
所以,我现在有一个工作程序。但是,它由一个线程运行,我想让它多线程。我打算为地图上的每个单元格创建一个线程。
我知道这有点矫枉过正,但无论如何我都想这样做。这样,我可以拥有这些小的原子碎片,细胞,所有这些都可以异步工作,它们可以在飞行中互相插入,就好像把拼图拼凑起来一样。
这就是基本的想法。我想让细胞尽可能自主。我试图实现这个:我为每个单元格创建了一个swing计时器,当计时器触发时,我启动了一个线程让单元格进行操作。我还为每个单元格添加了锁定,这样shtuff就不会搞砸了。多个邻居尝试访问同一个小区。这个问题是每次单元格尝试执行某些操作时都会调用一个新线程。我知道这是非常消耗资源(第一手经验)所以我想以某种方式为每个单元格创建持久线程。每个线程都会处理它自己的单元的计时器,动作监听器,并在计时器触发时运行它的单元格的动作。这些线程基本上永远不会停止。我需要帮助,因为我不确定如何实现这样的系统。
编辑:澄清问题:我需要一个侦听计时器事件的线程示例,并在计时器触发时运行。这应该都发生在线程内。答案 0 :(得分:2)
我认为增加Thread
的数量只有在增加cpu核心时才有意义。您可以考虑使用超线程。因此,例如,如果你有一个启用了HT的四核CPU,那么它可以同时处理8 Thread
个。
另一件事是,这通常是以另一种方式完成的。假设你有10个物体(植物,食草动物,等等)。每个都有calculateNextState()
之类的方法。您只需计算10个对象的循环中的所有状态,然后当您为所有状态创建新状态时,您将刷新GUI。你无限制地做这个广告。您不需要计时器,只需要考虑计算所需的时间。
示例:
你有一种食草动物,其速度为10个单位(x轴)。其位置为(2,4)
上次计算与now()
之间经过的时间为 200ms 您将其位置更新为(4,4)
。等等...
至于并行化:您可以创建ThreadPool
并将Runnable
推入其中。事实上,每个Runnable
都是计算GameObject
的下一个状态的函数(我刚刚给出了你所拥有的生物的名字)。当所有ThreadPool
完成工作后,您可以更新GUI。
主要的是:如果您为计算设置固定时间,则无法真正更新gui上的任意数量的对象,因为它可能需要比您设置的时间更长。< / p>
示例:如果您说“我想每秒更新一次”,但您有5.000.000个对象,则计算可能需要2秒。这就是为什么我建议你应该使用我描述的模型。所有状态计算都应该是可并行化的,因此您可以在线程之间分配工作。
然而,有一点需要注意:你必须考虑到2种不同的植物/食草动物试图扩展到上一次计算后空的同一个细胞的情况。答案 1 :(得分:1)
不妨写下我自己的答案。
首先:您将无法使用纯实体线程,基于计时器的方法准确地表示您的模拟世界。
原因如下:
当然,左侧是世界模型和右侧,是基于您的方法实现的简单线程模型。
现在,要指出的重点是:
从这三点来看,我希望很明显模型是不兼容的。
你可能尝试使你的世界模型适应线程模型,但是你会遇到改变你的世界以适应实现的问题。例如,您提到的锁定 - 它们会改变您的世界的运作方式,具体取决于您的方法。
当然,如果在特定情况中对此没问题,请继续(这意味着您对速度减少,通常伴随着大量的线程,以及同步带来的所有好问题)。
但是,在一般情况下,您应该根据您的规范选择实施,而不是相反。
如果你注意上述内容,你有很多选择,其中大部分都依赖于Adam Arold所描述的离散模拟步骤,并由clwhisk进行了细化。这些模型包括:
ThreadPoolExecutor
或ForkJoinPool
处理工作单位的有限数量的工作线程,答案 2 :(得分:0)
您没有定义持久性线程对您来说意味着什么。
在当前的计算机和操作系统上,{p> Threads与processes不一样persistent(请参阅application checkpointing)。例如,它们在单词的Java serializable中不是sense。您需要实现自己的计算持久性实现(而不是线程)。在continuation passing style中设计您的计划可能会有所帮助。拥有自己的持久计算概念(例如,让每个线程运行一个小循环,其状态是持久的和可序列化的。)
不要想到有数千(甚至数百)个线程;它们是昂贵的资源。考虑使用一个小thread pool(最多几十个线程)。线程池的大小应该是可配置的,不应该取决于数据的大小(或者你正在计算的问题)。
您可能需要考虑futures and promises,例如Java futures(在小型线程池中共享线程)。
答案 3 :(得分:0)
正如亚当的回答所暗示的那样,这通常是通过时间步长完成的。每个生物都有倒计时到下一个动作,并且最简单的形式是倒计时将是一个递减的整数,calculateNextState()
在每一步都调用一切。但是你有更多的灵活性。例如,将生物存储在按(双)倒计时排序的优先级队列中。您可以通过在队列中混洗他们的顺序来随机打破关系,或者在倒计时中添加随机元素。最重要的是,您可以使用常规方法完全控制模拟。线程不是工作的工具 - 它们听起来像是做了额外的工作,每次有并行任务时都应该使用它,但事实并非如此。