Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.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#_Loops_For Loop_Parallel Processing_Parallel.foreach - Fatal编程技术网

C# 了解循环和局部变量的并行计算

C# 了解循环和局部变量的并行计算,c#,loops,for-loop,parallel-processing,parallel.foreach,C#,Loops,For Loop,Parallel Processing,Parallel.foreach,我是并行计算的新手,在运行并行计算时遇到了一些问题。 我正在尝试同时访问多个网站,获取HTML并在多个SQLite数据库中注册它们。 在我更精确地检查结果之前,一切似乎都正常。 我注意到,在0到20的一个循环中,代码在循环的共享部分输入了20次,在本地部分只输入了16次。因此,缺少4个结果。 为了理解这个问题,我做了一个经验,我只放了两个计数器。一个在全球,另一个在本地。全局计数的输出为20,局部计数为1!之后,在全局部分返回到本地部分之前,我先睡了2秒钟。在这种情况下,全局计数的输出是20,而

我是并行计算的新手,在运行并行计算时遇到了一些问题。 我正在尝试同时访问多个网站,获取HTML并在多个SQLite数据库中注册它们。 在我更精确地检查结果之前,一切似乎都正常。 我注意到,在0到20的一个循环中,代码在循环的共享部分输入了20次,在本地部分只输入了16次。因此,缺少4个结果。 为了理解这个问题,我做了一个经验,我只放了两个计数器。一个在全球,另一个在本地。全局计数的输出为20,局部计数为1!之后,在全局部分返回到本地部分之前,我先睡了2秒钟。在这种情况下,全局计数的输出是20,而局部计数的输出是13!你能解释一下我做错了什么吗

static void ParalellCalc()
        {
            var tm = new Stopwatch();
            tm.Start();
            int count = 0;
            int count2 = 0;
            var parl = Parallel.For<int>(0, 20, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount}, () => 0, (i, state, Enrada) =>
            {
                count++;
                Thread.Sleep(2000);
                return Enrada;
            },
            (x) =>
            {
                count2++;
            }
            );

            tm.Stop();
            Console.WriteLine(tm.Elapsed);
            Console.WriteLine("Global: " + count.ToString());
            Console.WriteLine("Local: " + count2.ToString());
            Console.WriteLine(tm.Elapsed);
            tm.Reset();
        }
编辑: 我研究了你的建议,并对联锁的.递增计数器做了相同的示例。产生的结果完全相同。如果我删除Thread.Sleep2000,第二个计数器将产生1!?如果不删除,则生成16的结果。在所有情况下,第一个计数器显示20的值。有人能解释吗

static void ParalellCalc()
        {
            var tm = new Stopwatch();
            tm.Start();
            int count = 0;
            int count2 = 0;
            var parl = Parallel.For<int>(0, 20, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount}, () => 0, (i, state, Enrada) =>
            {
                Interlocked.Increment(ref count);
                return Enrada;
            },
            (x) =>
            {
                Interlocked.Increment(ref count2);
            });

            tm.Stop();
            Console.WriteLine(tm.Elapsed);
            Console.WriteLine("Global: " + count.ToString());
            Console.WriteLine("Local: " + count2.ToString());
            Console.WriteLine(tm.Elapsed);
            tm.Reset();
        } 

For并行执行您传入的操作,这不是保证,但在本例中很可能是真的。所以首先要注意的是,parallel.for中存在一个竞争条件,它可能通过从多个线程访问和写入计数器来实现。通过使用锁定机制(如lockobj)对代码的计数器++部分进行加密,您应该能够解决争用条件。

Parallel.For执行并行传递的操作不保证,但在这种情况下很可能是真的。所以首先要注意的是,parallel.for中存在一个竞争条件,它可能通过从多个线程访问和写入计数器来实现。通过使用锁定机制(如lockobj)对代码的计数器++部分进行加密,您应该能够解决争用条件。

++运算符不是线程安全的,因为它不是原子的。是线程安全的。Interlocked.Incrementref count而不是count++和count2的相同可能会修复计数。

++运算符不是线程安全的,因为它不是原子的。是线程安全的。Interlocked.Incrementref count而不是count++和count2的相同可能会修复计数。

并行。for方法通过将工作负载拆分为分区来并行化工作负载。分区的数量和每个分区的大小由内部启发式算法确定。您的实验表明,根据每个项目的处理持续时间,20个项目的工作负载只能在1个分区中分割,或者在16个分区中分割。通过添加Thread.Sleep2000这一行,您将工作负载从非常轻变为非常重,因此会创建更多的分区来平衡工作负载。这16个分区通常由少于16个线程处理,因为每个线程处理多个分区

为了更好地理解如何并行。对于works,您应该记录比两个计数器count和count2更多的信息。您还应该为每个线程使用一个带有每个线程键的计数器。

Parallel.for方法通过将工作负载拆分为分区来并行化工作负载。分区的数量和每个分区的大小由内部启发式算法确定。您的实验表明,根据每个项目的处理持续时间,20个项目的工作负载只能在1个分区中分割,或者在16个分区中分割。通过添加Thread.Sleep2000这一行,您将工作负载从非常轻变为非常重,因此会创建更多的分区来平衡工作负载。这16个分区通常由少于16个线程处理,因为每个线程处理多个分区


为了更好地理解如何并行。对于works,您应该记录比两个计数器count和count2更多的信息。您还应该为每个线程使用一个计数器,并使用每个线程的键。

您确定要并行而不是异步执行此操作吗?下载数据是一个I/O绑定的操作,不太可能从并行性中获益匪浅。Parrelell不管理线程,因此每次睡眠可能需要2秒,但也可能更长,具体取决于池如何管理。基本上,它创建一个线程,然后在工作完成时将其交给一个池来管理。仅仅因为您先创建了一个线程并不意味着管理器将首先或甚至下一个开始处理该线程。并行类是启动I/O操作的错误工具。当您的CPU有工作要做,而不是远程web服务器的CPU有工作要做时,请使用这个类。这项工作的正确工具是。这个库提供了一整套选项,可以处理CPU限制和I/O限制的操作,并允许您进行协同工作

精确控制并发级别。@DanielMann我从来没有用异步的方式思考过。你有什么建议?异步获取HTML并触发一个事件,在下载url时将数据保存在数据库中?@TheodorZoulias这里的问题不是I/O过程的管理。我认为我的问题不在于此。我写数据没有问题。我的问题是,每个线程都没有返回到本地函数。这个例子证明并不是所有线程都通过循环的本地部分。你能解释一下为什么没有Thread.Sleep2000,循环只在本地部分准时进入吗?致以最诚挚的问候。您确定要并行而不是异步执行此操作吗?下载数据是一个I/O绑定的操作,不太可能从并行性中获益匪浅。Parrelell不管理线程,因此每次睡眠可能需要2秒,但也可能更长,具体取决于池如何管理。基本上,它创建一个线程,然后在工作完成时将其交给一个池来管理。仅仅因为您先创建了一个线程并不意味着管理器将首先或甚至下一个开始处理该线程。并行类是启动I/O操作的错误工具。当您的CPU有工作要做,而不是远程web服务器的CPU有工作要做时,请使用这个类。这项工作的正确工具是。该库提供了一整套选项,可以处理CPU限制和I/O限制的操作,并允许您精确控制并发级别。@DanielMann我从来没有想过异步方式。你有什么建议?异步获取HTML并触发一个事件,在下载url时将数据保存在数据库中?@TheodorZoulias这里的问题不是I/O过程的管理。我认为我的问题不在于此。我写数据没有问题。我的问题是,每个线程都没有返回到本地函数。这个例子证明并不是所有线程都通过循环的本地部分。你能解释一下为什么没有Thread.Sleep2000,循环只在本地部分准时进入吗?致以最诚挚的问候。您最好使用@Renat的联锁解决方案,而不是在counter++语句周围使用锁。@Flydog57虽然是真的,但我认为OP使用了他发布的代码作为示例,他提到SQL数据库是他的实际用例。因此,我认为在这种情况下,提供更通用的锁方法更有用。但我不理解@EikeS。每个线程都不应该进入本地部分?当循环进入本地部分时,我从全局部分返回的数据在此上下文中没有被锁定?您最好使用@Renat的联锁解决方案,而不是在counter++语句周围使用锁。@Flydog57虽然是真的,但我认为OP使用了他发布的代码作为示例,他提到SQL数据库是他的实际用例。因此,我认为在这种情况下,提供更通用的锁方法更有用。但我不理解@EikeS。每个线程都不应该进入本地部分?当循环进入本地部分时,我从全局部分返回的数据在此上下文中没有被锁定?是的,你说得对。循环不是线程安全的,我现在知道了。但我的问题是。独立于代码是否是线程安全的,在进程的最后我不应该让count=20和count2=20?循环不应该每个线程进入本地部分一次?是的,你说得对。循环不是线程安全的,我现在知道了。但我的问题是。独立于代码是否是线程安全的,在进程的最后我不应该让count=20和count2=20?循环不应该每个线程进入本地部分一次?很好的解释Theodor,我想我现在明白了。您是否建议使用一些技术来确保每个线程在循环的本地部分单独处理?@MarcoTeixeira是的。你可以创建和使用你自己的,或者,如果你想要绝对控制,不要使用并行!您可以手动启动线程,或者使用Task.Run.threads的线程池线程。很好的解释,Theodor,我想我现在明白了。您是否建议使用一些技术来确保每个线程在循环的本地部分单独处理?@MarcoTeixeira是的。你可以创建和使用你自己的,或者,如果你想要绝对控制,不要使用并行!您可以手动启动线程,也可以在Task.Run中使用线程池线程。