您在这里面临的问题称为race conditions
,当多个 CPU 内核将相同的变量加载到各自的缓存中,处理它,然后将值写回 RAM 时会发生这种情况。显然,在此期间,写回 RAM 的值可能已经过时了(例如,当核心在变量被另一个值覆盖之前加载变量时)
首先:我不会使用b++
,而是int i = Interlocked.Increment(ref b);
。Interlocked.Increment 确保没有 2 个线程尝试同时增加相同的值。结果是将增加的值保存到变量i
中。这非常重要,因为您需要该值在 for 循环的每次迭代中保持不变,否则这是不可能的,因为其他线程将递增该变量。
接下来是您的变量f
和a
(定义为 For-iterator)。忘记f
,a
改用。
f = 2 * b + 1; // wrong
a = 2 * b + 1; // correct
最后: System.Collections.Generic.List不是,我重复一遍(因为它很重要)不是线程安全的。有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx 。
Primes.Add(f); // will likely break something
lock (Primes) // LOCK the list
{
Primes.Add(a); // don't forget, we're using 'a' instead of 'f' now
}
该lock
关键字仅接受引用类型变量作为参数,这是因为锁定变量不会阻止另一个线程访问它。相反,您可以将其想象为在引用之上设置一个标志,以便向其他线程发出信号I'm working here, please do not disturb!
当然,如果另一个线程在Primes
没有事先要求锁定它的情况下尝试访问,该线程仍然可以访问该变量。
不过,您应该已经学会了所有这些,因为 Parallel Prime Sieve 是第一次学习多线程时最常见的初学者练习之一。
编辑:
完成上述所有步骤后,程序不应该乱跑;但这并不意味着解决方案是正确的,或者您将获得加速,因为您的许多线程将做重复的工作。
假设Thread a
有责任标记每个 3 的倍数,而Thread n
有责任标记 9 的倍数。当顺序运行时,当Thread n
开始处理 9 的倍数时,它会看到 9 已经是另一个的倍数 (素数)数。但是,由于您的代码现在是并行的,因此无法保证Thread n
在开始工作时会标记 9 。更不用说 - 因为 9 可能没有被标记 - 可能会被添加到素数列表中。
因此,您必须依次找到 1 和 的平方根之间的所有素数To
。为什么是平方根To
?那是你必须自己找出来的东西。
一旦你找到了从 1 到 的平方根的所有素数To
,你就可以开始你的平行素数筛,以便使用之前找到的所有素数来找到剩余的素数。
最后一个值得注意的一点是,只有在填充之后Primes
才应该构建它。那是因为: Trues
1.你的线程只需要处理 2 的平方根的倍数To
,因此在当前实现中不会在根之外添加更多元素Primes
。
2.如果您选择让您的线程超出根目录,您将面临一个问题,即您的一个线程将Primes
在另一个线程将该数字标记为非素数之前不久添加一个非素数,这不是什么你要。
3.即使你很幸运,所有元素Primes
确实都是 1 和 之间的素数To
,它们也不一定是有序的,需要Primes
先排序。