Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 因为有不寻常的行为_C#_Math_For Loop_Parallel Processing - Fatal编程技术网

C# 因为有不寻常的行为

C# 因为有不寻常的行为,c#,math,for-loop,parallel-processing,C#,Math,For Loop,Parallel Processing,我试图将这个基本筛选函数转换为并行函数,这样它就可以利用多个核 然而,当我运行它时,b变量的值似乎随机跳转,甚至根本不改变,特别是对于to的更高值 static List<long> Sieve(long To) { long f = 0; To /= 2; List<long> Primes = new List<long>(); bool[] Trues = new bool[To]; if (To > 0

我试图将这个基本筛选函数转换为并行函数,这样它就可以利用多个核

然而,当我运行它时,b变量的值似乎随机跳转,甚至根本不改变,特别是对于to的更高值

static List<long> Sieve(long To)
{
    long f = 0;
    To /= 2;

    List<long> Primes = new List<long>();
    bool[] Trues = new bool[To];

    if (To > 0)
        Primes.Add(2);

    long b = 0;

    Parallel.For(1L, To, a =>
    {
        b++;

        if (Trues[b])
            return;

        f = 2 * b + 1;
        Primes.Add(f);

        for (long j = f + b; j < To; j += f)
            Trues[j] = true;
    });

    return Primes;
}
静态列表筛选(长到)
{
长f=0;
To/=2;
列表素数=新列表();
bool[]Trues=新bool[To];
如果(到>0)
添加(2);
长b=0;
平行。对于(1L,至,a=>
{
b++;
if(真[b])
返回;
f=2*b+1;
加上(f);
对于(长j=f+b;j

发生了什么,如何阻止这种情况发生?

b
是跨线程共享的。如果多个线程同时处理这个糟糕的变量,您希望发生什么


似乎
b
a
在代码中总是相等的(或相差一个)。使用
a
。并同步访问所有其他共享状态(如列表)。

欢迎来到多线程的奇妙世界

马上,我就可以看到循环的每个迭代都会执行
b++
,然后在整个过程中使用
b
。这意味着循环的每个迭代都将在所有其他迭代中修改
b
的值

您可能想做的是使用内联函数中提供的
a
变量,这正是您试图使用
b
所做的。如果情况并非如此,那么您应该先锁定
b
并将其值复制到局部(每个迭代)变量,然后再对其执行操作

试试这个,如果这是你想做的,请告诉我:

static List<long> Sieve(long To)
{
    To /= 2;

    List<long> Primes = new List<long>();

    if (To > 0)
        Primes.Add(2);

    Parallel.For(1L, To, a =>
    {
        long f = 2 * a + 1;
        Primes.Add(f);
    });

    Primes.Sort();

    return Primes;
}
静态列表筛选(长到)
{
To/=2;
列表素数=新列表();
如果(到>0)
添加(2);
平行。对于(1L,至,a=>
{
长f=2*a+1;
加上(f);
});
Primes.Sort();
返回素数;
}

您在这里面临的问题称为
竞争条件
,这是当多个CPU核心将同一变量加载到各自的缓存中,处理它,然后将值写回RAM时发生的情况。显然,写回RAM的值在此期间可能已经很旧了(比如当一个内核在被另一个值覆盖之前加载变量)

首先:我不会使用
b++
,而是
inti=Interlocked.Increment(参考b)取而代之。增量确保没有两个线程试图同时增加相同的值。结果是递增的值,该值将保存到变量
i
中。这是非常重要的,因为您需要该值在for循环的每次迭代中保持不变,否则这是不可能的,因为其他线程将递增该变量

接下来是变量
f
a
(定义为For迭代器)。忘记
f
,改用
a

f = 2 * b + 1; // wrong
a = 2 * b + 1; // correct
最后:System.Collections.Generic.List不是线程安全的,我重复一遍(因为它很重要)。有关更多详细信息,请参阅

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
关键字只接受引用类型变量作为参数,这是因为锁定变量不会阻止另一个线程访问它。相反,您可以将其想象为在引用顶部设置一个标志,以便向其他线程发出信号
我正在这里工作,请不要打扰

当然,如果另一个线程试图访问
Primes
,而不事先要求锁定它,那么该线程仍然能够访问该变量

不过,您应该已经了解了所有这些内容,因为在第一次学习多线程时,并行素数筛是最常见的初学者练习之一

编辑:

完成上述所有步骤后,程序不应运行异常然而这并不意味着解决方案是正确的,也不意味着您将获得加速,因为您的许多线程将进行重复工作

假设
线程a
负责标记3的每一个倍数,而
线程n
负责标记9的倍数。当按顺序运行时,当线程n开始处理9的倍数时,它将看到9已经是另一个(素数)的倍数。但是,由于您的代码现在是并行的,不能保证
线程n
开始工作时会标记9。更不用说——因为9可能没有标记——可能会被添加到素数列表中

因此,您必须按顺序查找
to
的1和平方根之间的所有素数。为什么将
的平方根设置为
?这是你必须自己去发现的

一旦找到了从1到
的平方根的所有素数,就可以使用以前找到的所有素数启动并行素数筛选,以找到其余的素数

最后值得注意的一点是,
Primes
只应在
Trues
填充后构建。那是因为:

1。您的线程只需处理
的平方根的2的倍数,因此在当前实现中,不会在根之外向
素数添加更多元素

2。如果您选择让线程超出根,您将面临一个问题,即在另一个线程将该数字标记为非素数之前不久,您的一个线程将向
素数添加一个非素数