好吧,我正在使用Parallel ForEach处理大量的颜色(3500万个索引器)。
我正在使用Partitioner.Create来实现这一目标。但是发生了意外情况:
private Color32[] MainGeneration(Color32[] proccesed, Color32[] dupe, int width, int height)
{
int i = 0;
Parallel.ForEach(Partitioner.Create(0, totalIndexes), item =>
{
int x = i % width;
int y = height - i / width - 1;
dupe[i] = (UnityEngine.Color)MapIteration((Color)((Color)(UnityEngine.Color)proccesed[i]).Clone(), i, x, y)
++i;
if (i % updateInterlockedEvery == 0)
{
++currentIndex; //Interlocked.Increment(ref currentIndex);
}
});
// If Parallel.ForEach is blocking, why this is outputting me 12 when total indexes value is 35.000.000?
Debug.Log(i);
return dupe;
}
正如我在评论中写道,为什么会这样?
这种预期的行为是使用并行处理大型图像,而不仅仅是一小部分。
processed
包含完整图像。
dupe
包含一个空数组,它将在每次迭代时完成。
我在本地范围内进行所有操作以避免堆问题。
答案 0 :(得分:3)
您不需要Fiddle Here
using System.Collections.Concurrent;
using System.Threading.Tasks;
using UnityEngine;
public class Program
{
private void MainGeneration(
Color32[] source,
Color32[] target,
int width,
int height)
{
Parallel.ForEach(Partitioner.Create(source, true)
.GetOrderableDynamicPartitions(), colorItem =>
{
var i = colorItem.Key;
var color = colorItem.Value;
var x = i % width;
var y = height - i / width - 1;
target[i] = this.Map(color, i, x, y);
});
}
private Color32 Map(Color32 color, long i, long x, long y)
{
return color;
}
}
答案 1 :(得分:2)
++i;
实际上是类似这样的缩写:
temp = i + 1;
i = temp;
幸运的是,您使用int的时间不长,因此至少i = temp;
分配是原子的,解释起来很容易:)
如果两个线程都在执行++ i;这样的事情可能会发生(为简单起见,只考虑了两个线程):
//Let's say i == 0
Thread 2 calculates i + 1 //== 1
Thread 1 calculates i + 1 //== 1
Thread 1 sets i = 1;
Thread 2 sets i = 1;
所以在这里您可能希望我为2,但实际上到此为止,我为1。
如果要以线程安全的方式递增i,可以执行以下操作:
Interlocked.Increment(ref i);
正如您的currentIndex代码中所指出的那样,也应该像这样计算。
鉴于数字上的巨大差异,我看到了代码的另一个问题。如果线程的IsBackGround属性为true,则不在主线程上/不在主线程上报告在主线程之外发生的异常。为了防止这种情况,您应该尝试/捕获foreach中的内部块,以同样的方式计数异常。
甚至更好的是,在ConcurrentQueue / ConcurrentBag中获取异常列表:
// Use ConcurrentQueue to enable safe enqueueing from multiple threads.
var exceptions = new ConcurrentQueue<Exception>();
// Execute the complete loop and capture all exceptions.
Parallel.ForEach(data, d =>
{
try
{
// Cause a few exceptions, but not too many.
if (d < 3)
throw new ArgumentException($"Value is {d}. Value must be greater than or equal to 3.");
else
Console.Write(d + " ");
}
// Store the exception and continue with the loop.
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
Console.WriteLine();
// Throw the exceptions here after the loop completes.
if (exceptions.Count > 0) throw new AggregateException(exceptions);