System.Threading.Parallel 提供了几种实用的循环并行处理方法,让我们可以用非常简便的代码完成并发处理。
1. For
var datas = new[] { "a", "b", "c" };
Parallel.For(0, datas.Length, i =>
{
var s = datas[i];
Console.WriteLine(">>> Thread:{0}, {1}", Thread.CurrentThread.ManagedThreadId, s);
});
输出:
>>> Thread:3, a
>>> Thread:7, b
>>> Thread:5, c
我们还可以像 for(;; i += 2) 那样指定循环递增幅度。
var datas = new[] { "a", "b", "c", "d", "e" };
Parallel.For(0, datas.Length, 2, i =>
{
var s = datas[i];
Console.WriteLine(">>> Thread:{0}, {1}", Thread.CurrentThread.ManagedThreadId, s);
});
for (int i = 0; i < datas.Length; i += 2)
{
Console.WriteLine(datas[i]);
}
输出:
>>> Thread:3, a
>>> Thread:7, e
>>> Thread:4, c
a
c
e
For() 还提供了更多更复杂的重载,包括提供 init、finally、state 等操作。
比如,我们可以通过调用 ParallelState.Stop() 来停止后续处理。
var datas = new[] { "a", "b", "c", "d", "e" };
Parallel.For(0, datas.Length, (i, state) =>
{
state.Stop();
var s = datas[i];
Console.WriteLine(">>> Thread:{0}, {1}",
Thread.CurrentThread.ManagedThreadId, s);
});
输出:
>>> Thread:3, a
>>> Thread:7, b
>>> Thread:5, c
还可以像下面这样,添加初始化、结束处理委托,并传递额外的参数给并行代码。
var datas = new[] { "a", "b" };
Parallel.For<int>(0, datas.Length,
() =>
{
Console.WriteLine("Init {0}...", Thread.CurrentThread.ManagedThreadId);
return 123;
},
(i, state) =>
{
state.Stop();
var s = datas[i];
Console.WriteLine(">>> Thread:{0}, {1}, {2}",
Thread.CurrentThread.ManagedThreadId, s, state.ThreadLocalState);
},
(x) =>
{
Console.WriteLine("Finally {0}, {1}...", Thread.CurrentThread.ManagedThreadId, x);
});
输出:
Init 3...
Init 5...
>>> Thread:3, a, 123
>>> Thread:5, b, 123
Finally 3, 123...
Finally 5, 123...
2. Foreach
ForEach() 和 For() 的用法非常类似。
var datas = new[] { "a", "b", "c", "d", "e" };
Parallel.ForEach(datas, s =>
{
Console.WriteLine(">>> thread:{0}, {1}",
Thread.CurrentThread.ManagedThreadId, s);
});
输出:
>>> thread:3, a
>>> thread:6, b
>>> thread:5, c
>>> thread:6, e
>>> thread:7, d
作者善解人意,同样提供了多种重载,包括获取索引位置以及并发状态管理等等。
var datas = new[] { "a", "b", "c", "d", "e" };
Parallel.ForEach(datas, (s, i, state) =>
{
Console.WriteLine(">>> thread:{0}, index:{1}, {2}",
Thread.CurrentThread.ManagedThreadId, i, s);
});
输出:
>>> thread:3, index:0, a
>>> thread:7, index:3, d
>>> thread:6, index:2, c
>>> thread:7, index:4, e
>>> thread:5, index:1, b
3. Invoke
Invoke 针对的是 "并发逻辑" 而非 "并发数据"。当然,我们也可以将 "并发逻辑" 打包成数据集合(Delegate[]) 提交给 For/Foreach 执行,只不过 Invoke 更简便一些。
Parallel.Invoke(new Action[]
{
() => Console.WriteLine(">>> thread:{0}, {1}", Thread.CurrentThread.ManagedThreadId, 1),
() => Console.WriteLine(">>> thread:{0}, {1}", Thread.CurrentThread.ManagedThreadId, "a"),
() => Console.WriteLine(">>> thread:{0}, {1}", Thread.CurrentThread.ManagedThreadId, typeof(object)),
});
输出:
>>> thread:3, 1
>>> thread:5, System.Object
>>> thread:4, a
改用 TPL / Invoke 写蜘蛛(Spider/Crawler)抓取代码,可以大大简化代码样式,少了许多线程管理调度