我有大量的状态机。有时,状态机需要从一个状态移动到另一个状态,这可能是便宜或昂贵的,可能涉及数据库读写等。
这些状态更改是由于来自客户端的传入命令而发生的,并且可以随时发生。
我希望将工作负载并行化。我想要一个队列说'将这台机器从这个状态移动到这个状态'。显然,任何一台机器的命令都需要按顺序执行,但如果我有很多线程,我可以并行移动许多机器。
我可以为每台状态机创建一个线程,但状态机的数量依赖于数据,可能有数百或数千;我不希望每个状态机都有一个专用线程,我想要一个某种类型的池。
如何拥有一个工作池,但要确保每个状态机的命令都是按顺序处理的?
更新 :所以假设Machine
实例有一个未完成命令的列表。当线程池中的执行程序完成使用命令时,如果它具有更多未完成的命令,它会将Machine
放回到线程池的任务队列中。所以问题是,当你附加第一个命令时,如何以原子方式将Machine
放入线程池中?并确保这一切都是线程安全的吗?
答案 0 :(得分:2)
我建议你这个场景:
Executors.newFixedThreadPool
HashMap
),为每个状态机保留一个Semaphore
。信号量的值为1,并且公平信号量保持序列semaphore.aquire()
,并在run方法结束时添加semaphore.release()
。使用线程池的大小,您将控制并行度。
答案 1 :(得分:2)
我建议采用另一种方法。不使用线程池来移动状态机中的状态,而是使用线程池来处理所有事情,包括执行工作。在做了一些导致状态改变的工作之后,应该将状态改变事件添加到队列中。处理完状态更改后,应将另一个do-work事件添加到队列中。
假设状态转换是由工作驱动的,反之亦然,则无法进行顺序处理。
将信号量存储在特殊地图中的想法非常危险。地图必须同步(添加/删除objs是线程不安全的)并且进行搜索(可能在地图上同步)然后使用信号量的开销相对较大。
此外 - 如果你想在你的应用程序中使用多线程架构,我认为你应该一路走下去。混合使用不同的架构可能会在以后引起麻烦。
答案 2 :(得分:1)
每台机器都有一个线程ID。产生所需数量的线程。让所有线程贪婪地处理来自全局队列的消息。每个线程锁定当前消息的服务器以供其自己独占使用(直到它完成处理当前消息及其队列中的所有消息),其他线程将该服务器的消息放入其内部队列。
编辑:处理消息伪代码:
void handle(message)
targetMachine = message.targetMachine
if (targetMachine.thread != null)
targetMachine.thread.addToQueue(message);
else
targetMachine.thread = this;
process(message);
processAllQueueMessages();
targetMachine.thread = null;
处理消息Java代码:(我可能会略微过度复杂,但这应该是线程安全的)
/* class ThreadClass */
void handle(Message message)
{
// get targetMachine from message
targetMachine.mutexInc.aquire(); // blocking
targetMachine.messages++;
boolean acquired = targetMachine.mutex.aquire(); // non-blocking
if (acquired)
targetMachine.threadID = this.ID;
targetMachine.mutexInc.release();
if (!acquired)
// can put this before release, it may speed things up
threads[targetMachine.threadID].addToQueue(message);
else
{
process(message);
targetMachine.messages--;
while (true)
{
while (!queue.empty())
{
process(queue.pop());
targetMachine.messages--;
}
targetMachine.mutexInc.acquire(); // blocking
if (targetMachine.messages > 0)
{
targetMachine.mutexInc.release();
Thread.sleep(1);
}
else
break;
}
targetMachine.mutex.release();
targetMachine.mutexInc.release();
}
}