首页 > IT职业 > IT图书 > C#基础提升系列——C#任务和并行编程
ParallelLoopState.Break()方法的示例如下: public static void StopParallelForEarly()
{
ParallelLoopResult result = Parallel.For(0, 10,
(int i, ParallelLoopState pls) =>
{
log("开始:" + i);
if (i > 5)
{
pls.Break();
log("中断:" + i);
}
Task.Delay(5000).Wait();
log("结束:" + i);
});
Console.WriteLine("循环是否完成运行:" + result.IsCompleted);
Console.WriteLine("最小迭代索引:" + result.LowestBreakIteration);
} 上述执行结果如下:
开始:0 任务ID: 1 线程ID: 1
开始:2 任务ID: 2 线程ID: 3
开始:4 任务ID: 3 线程ID: 5
开始:6 任务ID: 4 线程ID: 4
中断:6 任务ID: 4 线程ID: 4
开始:8 任务ID: 5 线程ID: 6
中断:8 任务ID: 5 线程ID: 6
开始:1 任务ID: 6 线程ID: 8
开始:3 任务ID: 7 线程ID: 9
开始:5 任务ID: 8 线程ID: 10
结束:2 任务ID: 2 线程ID: 3
结束:4 任务ID: 3 线程ID: 5
结束:0 任务ID: 1 线程ID: 1
结束:8 任务ID: 5 线程ID: 6
结束:6 任务ID: 4 线程ID: 4
结束:1 任务ID: 6 线程ID: 8
结束:3 任务ID: 7 线程ID: 9
结束:5 任务ID: 8 线程ID: 10
循环是否完成运行:False
最小迭代索引:6
-----程序执行完毕----- 通过结果可以说明:
迭代虽然调用了Break()方法,但是其他迭代仍然可以同时运行。并且在中断前开始的所有任务都可以继续运行,直到结束。注意:Break()方法不会停止任何已经开始执行的迭代。
注:使用Break()方法时,常常需要结合LowestBreakIteration 和ShouldExitCurrentIteration 属性,关于具体用法,可以参见官方示例和说明。
Parallel.For()对线程初始化Parallel.For()方法使用多个线程来执行循环,如果需要对每个线程进行初始化,可以使用Parallel.For()的另一个重载方法,该方法定义如下:
public static System.Threading.Tasks.ParallelLoopResult For (
int fromInclusive,
int toExclusive,
Func localInit,
Func body,
Action localFinally); 除了前两个参数外,第三个委托参数Func,返回每个任务的本地数据的初始状态,这个方法仅用于执行迭代的每个线程调用一次。
Parallel.For()方法的第四个参数Func为循环体定义委托,该Func的第一个参数是迭代索引,第二个参数ParallelLoopState允许停止循环,第三个参数TLocal接收从Func方法返回的值,最后一个参数为返回的类型,该类型和Func中的TLocal要一致。
Parallel.For()方法的第五个参数Action为线程退出调用的方法,该方法接收一个TLocal类型的值,该值来自于第四个参数返回的结果。
通过Parallel.For()方法的定义可以看出,它的后三个参数都是基于同一个泛型类型(Tlocal)进行操作的,因此在实际使用时,这三个委托所传入的方法的参数和返回值的类型要保持一致。
示例代码如下:
public static void ParallelForWithInit()
{
Parallel.For(0, 10, () =>
{
log("初始化线程");
return "线程ID:" + Thread.CurrentThread.ManagedThreadId;
},
(i, pls, str1) =>
{
log($"迭代调用:{i} ,字符串:{str1}");
Task.Delay(5000).Wait();
return "i " + i;
},
(str1) =>
{
log("finally " + str1);
});
} 在上述方法中,第一个委托的结果,将会作为参数传递给第二个委托的第三个参数,第二个委托的结果,又会作为参数传递给第三个委托。
该方法的执行结果如下:
初始化线程 任务ID: 2 线程ID: 3
初始化线程 任务ID: 3 线程ID: 4
初始化线程 任务ID: 4 线程ID: 5
迭代调用:6 ,字符串:线程ID:5 任务ID: 4 线程ID: 5
初始化线程 任务ID: 1 线程ID: 1
迭代调用:2 ,字符串:线程ID:3 任务ID: 2 线程ID: 3
迭代调用:4 ,字符串:线程ID:4 任务ID: 3 线程ID: 4
迭代调用:0 ,字符串:线程ID:1 任务ID: 1 线程ID: 1
初始化线程 任务ID: 5 线程ID: 6
迭代调用:8 ,字符串:线程ID:6 任务ID: 5 线程ID: 6
初始化线程 任务ID: 6 线程ID: 8
迭代调用:1 ,字符串:线程ID:8 任务ID: 6 线程ID: 8
初始化线程 任务ID: 7 线程ID: 9
迭代调用:3 ,字符串:线程ID:9 任务ID: 7 线程ID: 9
初始化线程 任务ID: 8 线程ID: 10
迭代调用:5 ,字符串:线程ID:10 任务ID: 8 线程ID: 10
初始化线程 任务ID: 9 线程ID: 11
迭代调用:7 ,字符串:线程ID:11 任务ID: 9 线程ID: 11
初始化线程 任务ID: 10 线程ID: 12
迭代调用:9 ,字符串:线程ID:12 任务ID: 10 线程ID: 12
finally i 8 任务ID: 5 线程ID: 6
finally i 0 任务ID: 1 线程ID: 1
finally i 2 任务ID: 2 线程ID: 3
finally i 4 任务ID: 3 线程ID: 4
finally i 6 任务ID: 4 线程ID: 5
finally i 1 任务ID: 6 线程ID: 8
finally i 3 任务ID: 7 线程ID: 9
finally i 5 任务ID: 8 线程ID: 10
finally i 7 任务ID: 9 线程ID: 11
finally i 9 任务ID: 10 线程ID: 12
-----程序执行完毕----- Parallel.ForEach()Parallel.ForEach()方法遍历实现了IEnumerable的集合,其方式类似于foreach语句,但以异步方式遍历。
public static void ParallelForEach()
{
string[] data = { "zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve" };
ParallelLoopResult result = Parallel.ForEach(
data, s => { Console.WriteLine(s); });
//如果需要终端循环,可以使用重载版本和ParallelLoopState参数
ParallelLoopResult result2 = Parallel.ForEach(
data, (s, pls, l) => { Console.WriteLine(s); });
} 如果需要中断循环,可以使用ForEach()方法的重载版本和ParallelLoopState参数,其方式和For()方法相同。
Parallel.Invoke()同时调用多个方法如果多个任务将并行运行,就可以使用Parallel.Invoke()方法,它提供了任务并行性模式。Parallel.Invoke()方法允许传递一个Action委托的数组,在其中可以指定将运行的方法。
public static void ParallelInvoke()
{
Parallel.Invoke(Foo, Bar);
}
public static void Foo()
{
Console.WriteLine("Foo");
}
public static void Bar()
{
Console.WriteLine("Bar");
} Parallel总结一般来说,优先使用Parallel类进行并行处理,它既可以用于任务,也可以用于数据并行性。如果Parallel类不能很好的解决,或者需要细致的控制,或者不想等到Parallel类结束后再开始其他操作动作,就可以使用Task类。也可以结合使用Task类和Parallel类,具体根据情况而定。
WhenAny():只要有一个任务完成就返回调用任务。 WaitAny():阻塞调用任务,等待任何一个任务完成。Delay():指定从这个方法返回的任务完成前要等待的毫秒数。Yield():如果将释放CPU,从而允许其他任务运行,就可以调用Task.Yield()方法。该方法释放CPU,让其他任务运行。如果没有其他任务等待运行,调用Task.Yield()的任务就立即继续执行。否则,需要等到再次调度CPU,以调用任务。 本文后续会随着知识的积累不断补充和更新,内容如有错误,欢迎指正。
最后一次更新时间 :2018-07-30
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1065/viewspace-2811280/ ,如需转载,请注明出处,否则将追究法律责任。