多线程在任何情况下都很有用,其中单个线程必须等待资源,在此期间您可以运行另一个线程。这包括一个线程等待 I/O 请求或数据库访问,而另一个线程继续 CPU 工作。
如果可以将单个线程定位到不同的CPU(或内核),则多线程也很有用,因为它们随后真正并发运行,尽管它们通常必须共享数据,因此仍然存在一些争用。
我看不出为什么多线程数独求解器比单线程求解器更有效,仅仅是因为没有等待资源。一切都将在内存中完成。
但我记得我在Uni做的一些功课,它同样毫无用处(Fortran代码,看看当你以30度挖下一英里,然后以15度挖出另一英里时,隧道有多深 - 是的,我已经很老了:-)。关键是要表明你可以做到,而不是它有用。
转到算法。
我写了一个单线程求解器,它基本上在每个通道中运行一系列规则,以尝试填充另一个正方形。一个示例规则是:如果行 1 只有一个正方形可用,则该数字从行 1 中的所有其他数字中显而易见。
所有行,所有列,所有3x3迷你网格都有类似的规则。还有一些规则可以检查行/列的相交(例如,如果给定的正方形由于行而只能包含3或4,由于列而包含4或7,那么它就是4)。还有更复杂的规则,我不会在这里详细说明,但它们基本上与你手动解决它的方式相同。
我怀疑你在实现中有类似的规则(因为除了蛮力,我想不出其他方法来解决它,如果你使用了蛮力,你就没有希望了:-)。
我建议将每个规则分配给一个线程,并让它们共享网格。每个线程都会执行自己的规则,并且只执行该规则。
更新:
乔恩,根据你的编辑:
[编辑]我忘了提一下,要使用的线程数被指定为程序的参数,因此据我所知,它与谜题的状态没有任何关系......
此外,可能没有唯一的解决方案 - 有效的输入可能是一个完全空的电路板。我必须报告min(1000,解决方案数量)并显示其中一个(如果存在)
看起来你的老师不希望你根据规则进行拆分,而是根据分叉点(可以应用多个规则)进行拆分。
我的意思是,在解决方案的任何时候,如果有两个或更多可能的前进,你应该将每个可能性分配给一个单独的线程(仍然使用你的规则来提高效率,但同时检查每种可能性)。这将为您提供更好的并发性(假设线程可以在单独的CPU/内核上运行),因为不会有对电路板的争用;每个线程都将获得自己的副本。
此外,由于您要限制线程的数量,因此您必须使用一些线程池魔术来实现此目的。
我的建议是有一个工作队列和N个线程。当主线程启动所有工作线程时,工作队列最初为空。然后,主线程将开始的拼图状态放入工作队列中。
工作线程只需等待将状态放置在工作队列上,其中一个线程会抓取该状态进行处理。工作线程是您的单线程求解器,只需进行一次小的修改:当有 X 种可能性向前推进(X > 1)时,您的工作线程会将其中 X-1 放回工作队列,然后继续处理另一种可能性。
所以,假设只有一个解决方案(真正的数独:-)。第一个工作线程将在找不到任何分支的情况下减少解决方案,这将与您当前的情况完全相同。
但是,在移动 27 处有两种可能性(例如,3 或 4 可以进入左上角的单元格),您的线程将创建另一个具有第一种可能性的板(将 3 放入该单元格中)并将其放置在工作队列中。然后它会把4放在自己的副本中并继续。
另一个线程将拾取该单元中带有3的板并继续进行。这样,您就有两个线程同时运行,处理这两种可能性。
当任何线程确定其电路板不溶时,它会将其丢弃并返回到工作队列以进行更多工作。
当任何线程决定其电路板已解决时,它会通知可以存储它的主线程,覆盖任何以前的解决方案(首先找到的是解决方案)或将其丢弃(最后找到的是解决方案),然后工作线程返回到工作队列进行更多工作。在任一情况下,主线程都应增加找到的解决方案计数。
当所有线程都空闲并且工作队列为空时,main 将有或没有解决方案。它还将有一系列解决方案。
请记住,worker 和 main thread 之间的所有通信都需要互斥(我假设您根据问题中的信息知道这一点)。