【发布时间】:2020-03-30 21:49:16
【问题描述】:
我是并行计算的新手,在 C# 中运行 Parallel.For 时遇到了一些问题。 我正在尝试同时访问多个网站,获取 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();
}
编辑: 我考虑了你的建议,我用 Interlocked.Increment 做了同样的例子来增加计数器。产生的结果完全相同。如果我删除 Thread.Sleep(2000) 第二个计数器产生 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();
}
【问题讨论】:
-
您确定要并行而不是异步执行此操作吗?下载数据是一项 I/O 绑定操作,不太可能从并行性中受益匪浅。
-
Parrellel 不管理线程,因此每次睡眠可能为 2 秒,但也可能更长,具体取决于池的管理方式。基本上它会创建一个线程,然后将其交给一个池来管理工作何时完成。仅仅因为您首先创建了一个线程并不意味着经理将首先甚至下一个开始处理该线程。
-
Parallel类是启动 I/O 操作的错误工具。当您需要为 您的 CPU 工作而不是为远程 Web 服务器的 CPU 工作时,请使用此类。适合这项工作的工具是TPL Dataflow Library。该库提供了全套选项,可以处理 CPU 密集型和 I/O 密集型操作,并允许您精确控制并发级别。 -
@DanielMann 我从来没有以异步的方式思考过。你有什么建议?下载 url 时异步获取 HTML 并触发将数据保存在数据库中的事件?
-
@TheodorZoulias 这里的问题不是 I/O 进程的管理。我认为我的问题不在于。我写数据没有问题。我的问题是每个线程都没有返回到本地函数。做了这个例子,证明并非所有线程都经过循环的本地部分。你能解释一下为什么没有 Thread.Sleep(2000) 循环只能在本地部分准时进入吗?最好的问候。
标签: c# loops for-loop parallel-processing parallel.foreach