二、 并行循环的中断和跳出
当在进行循环时,偶尔会需要中断循环或跳出循环。下面是两种跳出循环的方法Stop和Break,LoopState是循环状态的参数。
/// <summary>
/// 中断Stop
/// </summary>
private void Demo5()
{
List<int> data = Program.Data;
Parallel.For(0, data.Count, (i, LoopState) =>
{
if (data[i] > 5)
LoopState.Stop();
Thread.Sleep(500);
Console.WriteLine(data[i]);
});
Console.WriteLine("Stop执行结束。");
}
/// <summary>
/// 中断Break
/// </summary>
private void Demo6()
{
List<int> data = Program.Data;
Parallel.ForEach(data, (i, LoopState) =>
{
if (i > 5)
LoopState.Break();
Thread.Sleep(500);
Console.WriteLine(i);
});
Console.WriteLine("Break执行结束。");
}
/// 中断Stop
/// </summary>
private void Demo5()
{
List<int> data = Program.Data;
Parallel.For(0, data.Count, (i, LoopState) =>
{
if (data[i] > 5)
LoopState.Stop();
Thread.Sleep(500);
Console.WriteLine(data[i]);
});
Console.WriteLine("Stop执行结束。");
}
/// <summary>
/// 中断Break
/// </summary>
private void Demo6()
{
List<int> data = Program.Data;
Parallel.ForEach(data, (i, LoopState) =>
{
if (i > 5)
LoopState.Break();
Thread.Sleep(500);
Console.WriteLine(i);
});
Console.WriteLine("Break执行结束。");
}
执行结果如下:

结论2:使用Stop会立即停止循环,使用Break会执行完毕所有符合条件的项。
三、并行循环中为数组/集合添加项
上面的应用场景其实并不是非常多见,毕竟只是为了遍历一个数组内的资源,我们更多的时候是为了遍历资源,找到我们所需要的。那么请继续看。
下面是我们一般会想到的写法:
private void Demo7()
{
List<int> data = new List<int>();
Parallel.For(0, Program.Data.Count, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Add(Program.Data[i]);
});
Console.WriteLine("执行完成For.");
}
private void Demo8()
{
List<int> data = new List<int>();
Parallel.ForEach(Program.Data, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Add(Program.Data[i]);
});
Console.WriteLine("执行完成ForEach.");
}
{
List<int> data = new List<int>();
Parallel.For(0, Program.Data.Count, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Add(Program.Data[i]);
});
Console.WriteLine("执行完成For.");
}
private void Demo8()
{
List<int> data = new List<int>();
Parallel.ForEach(Program.Data, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Add(Program.Data[i]);
});
Console.WriteLine("执行完成ForEach.");
}
看起来应该是没有问题的,但是我们多次运行后会发现,偶尔会出现错误如下:

这是因为List是非线程安全的类,我们需要使用System.Collections.Concurrent命名空间下的类型来用于并行循环体内。
| 类 | 说明 |
| BlockingCollection<T> | 为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻止和限制功能。 |
| ConcurrentBag<T> | 表示对象的线程安全的无序集合。 |
| ConcurrentDictionary<TKey, TValue> | 表示可由多个线程同时访问的键值对的线程安全集合。 |
| ConcurrentQueue<T> | 表示线程安全的先进先出 (FIFO) 集合。 |
| ConcurrentStack<T> | 表示线程安全的后进先出 (LIFO) 集合。 |
| OrderablePartitioner<TSource> | 表示将一个可排序数据源拆分成多个分区的特定方式。 |
| Partitioner | 提供针对数组、列表和可枚举项的常见分区策略。 |
| Partitioner<TSource> | 表示将一个数据源拆分成多个分区的特定方式。 |