那么我们上面的代码可以修改为,加了了ConcurrentQueue和ConcurrentStack的最基本的操作。
/// <summary>
/// 并行循环操作集合类,集合内只取5个对象
/// </summary>
private void Demo7()
{
ConcurrentQueue<int> data = new ConcurrentQueue<int>();
Parallel.For(0, Program.Data.Count, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Enqueue(Program.Data[i]);//将对象加入到队列末尾
});
int R;
while (data.TryDequeue(out R))//返回队列中开始处的对象
{
Console.WriteLine(R);
}
Console.WriteLine("执行完成For.");
}
/// <summary>
/// 并行循环操作集合类
/// </summary>
private void Demo8()
{
ConcurrentStack<int> data = new ConcurrentStack<int>();
Parallel.ForEach(Program.Data, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Push(Program.Data[i]);//将对象压入栈中
});
int R;
while (data.TryPop(out R))//弹出栈顶对象
{
Console.WriteLine(R);
}
Console.WriteLine("执行完成ForEach.");
}
/// 并行循环操作集合类,集合内只取5个对象
/// </summary>
private void Demo7()
{
ConcurrentQueue<int> data = new ConcurrentQueue<int>();
Parallel.For(0, Program.Data.Count, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Enqueue(Program.Data[i]);//将对象加入到队列末尾
});
int R;
while (data.TryDequeue(out R))//返回队列中开始处的对象
{
Console.WriteLine(R);
}
Console.WriteLine("执行完成For.");
}
/// <summary>
/// 并行循环操作集合类
/// </summary>
private void Demo8()
{
ConcurrentStack<int> data = new ConcurrentStack<int>();
Parallel.ForEach(Program.Data, (i) =>
{
if (Program.Data[i] % 2 == 0)
data.Push(Program.Data[i]);//将对象压入栈中
});
int R;
while (data.TryPop(out R))//弹出栈顶对象
{
Console.WriteLine(R);
}
Console.WriteLine("执行完成ForEach.");
}
ok,这里返回一个序列的问题也解决了。
结论3:在并行循环内重复操作的对象,必须要是thread-safe(线程安全)的。集合类的线程安全对象全部在System.Collections.Concurrent命名空间下。
四、返回集合运算结果/含有局部变量的并行循环
使用循环的时候经常也会用到迭代,那么在并行循环中叫做 含有局部变量的循环 。下面的代码中详细的解释,这里就不啰嗦了。
/// <summary>
/// 具有线程局部变量的For循环
/// </summary>
private void Demo9()
{
List<int> data = Program.Data;
long total = 0;
//这里定义返回值为long类型方便下面各个参数的解释
Parallel.For<long>(0, // For循环的起点
data.Count, // For循环的终点
() => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
(i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前索引,LoopState是循环状态,subtotal为局部变量名
{
subtotal += data[i]; // 修改局部变量
return subtotal; // 传递参数给下一个迭代
},
(finalResult) => Interlocked.Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
);
Console.WriteLine(total);
}
/// <summary>
/// 具有线程局部变量的ForEach循环
/// </summary>
private void Demo10()
{
List<int> data = Program.Data;
long total = 0;
Parallel.ForEach<int, long>(data, // 要循环的集合对象
() => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
(i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前元素,LoopState是循环状态,subtotal为局部变量名
{
subtotal += i; // 修改局部变量
return subtotal; // 传递参数给下一个迭代
},
(finalResult) => Interlocked.Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
);
Console.WriteLine(total);
}
/// 具有线程局部变量的For循环
/// </summary>
private void Demo9()
{
List<int> data = Program.Data;
long total = 0;
//这里定义返回值为long类型方便下面各个参数的解释
Parallel.For<long>(0, // For循环的起点
data.Count, // For循环的终点
() => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
(i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前索引,LoopState是循环状态,subtotal为局部变量名
{
subtotal += data[i]; // 修改局部变量
return subtotal; // 传递参数给下一个迭代
},
(finalResult) => Interlocked.Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
);
Console.WriteLine(total);
}
/// <summary>
/// 具有线程局部变量的ForEach循环
/// </summary>
private void Demo10()
{
List<int> data = Program.Data;
long total = 0;
Parallel.ForEach<int, long>(data, // 要循环的集合对象
() => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
(i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前元素,LoopState是循环状态,subtotal为局部变量名
{
subtotal += i; // 修改局部变量
return subtotal; // 传递参数给下一个迭代
},
(finalResult) => Interlocked.Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
);
Console.WriteLine(total);
}
结论4:并行循环中的迭代,确实很伤人。代码太难理解了。