2.Parallel.ForEach() 的使用
从 ForEach() 这个名字可以看出该方法是用来遍历泛型集合的,新建一个 ASP.NET Core Web应用的项目,如下:
在 Index.cshtml.cs 文件中增加一个 UserInfo.cs 的类,代码如下:
public class UserInfo
{
public int UserId { get; set; }
public string UserName { get; set; }
}
在 Index.cshtml.cs 文件的 IndexModel 类中添加测试方法 ParallelForEachDemo(),代码如下:
public void OnGet()
{
ParallelForEachDemo();
}
public string DemoStr; //定义页面取值的变量
public void ParallelForEachDemo()
{
//构造泛型集合数据
List<UserInfo> userList = new List<UserInfo>
{
new UserInfo{ UserId=1,UserName="张三" },
new UserInfo{ UserId=2,UserName="李四" },
new UserInfo{ UserId=3,UserName="王五" },
new UserInfo{ UserId=4,UserName="赵六" },
new UserInfo{ UserId=5,UserName="大师兄" }
};
//foreach 循环时间统计
string allName1 = string.Empty;
Stopwatch sw1 = new Stopwatch();
sw1.Start(); //计时开始
foreach (UserInfo user in userList)
{
allName1 += user.UserName + ",";
Thread.Sleep(10);//模拟一个耗时操作,以免看不到效果
}
sw1.Stop(); //计时结束
//Parallel.ForEach 循环时间统计
string allName2 = string.Empty;
Stopwatch sw2 = new Stopwatch();
sw2.Start(); //计时开始
Parallel.ForEach(userList, user => //多线程遍历
{
allName2 += user.UserName + ",";
Thread.Sleep(10);//模拟一个耗时操作,以免看不到效果
});
sw2.Stop(); //计时结束
//记录花费时间
DemoStr = string.Format("foreach 循环花费时间为:{0},Parallel.ForEach 循环花费时间为:{1}",
sw1.ElapsedMilliseconds, sw2.ElapsedMilliseconds);
}
在 Index.cshtml.cs 中输出 DemoStr 变量的值,代码如下:
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<br />
<p> @Model.DemoStr </p>
</div>
编译后运行,结果如下:
显然使用多线程更快,单线程和多线程的时间差距大概是5倍左右(不同CPU时间不同,这里仅代表本次执行结果)。
3.Parallel.Invoke() 的使用
在 Index.cshtml.cs 文件的 IndexModel 类中新增方法,分别访问 bing.com,360.cn,baidu.com 这3个网站, 然后统计响应的字符数,代码如下:
/// <summary>
/// Thread.CurrentThread.ManagedThreadId 用于显示当前的线程ID
/// </summary>
/// <param name="from"></param>
/// <param name="url"></param>
public void CountString(string from, string url)
{
long cnt = 0;
HttpWebRequest request = WebRequest.CreateHttp(url); //根据给定的网址创建一个请求
HttpWebResponse response = (HttpWebResponse)request.GetResponse(); //得到响应对象
cnt = response.ContentLength; //获取响应内容长度
//用table标签格式化输出便于查看
ContentStr += "<tr><td>" + from + "</td><td>" + Thread.CurrentThread.ManagedThreadId + "</td>" +
"<td>" + url + "</td><td>" + cnt + "</td></tr>";
return;
}
单线程顺序执行3次函数:
/// <summary>
/// 单线程执行3次函数取三个不同网址的内容
/// </summary>
/// <returns></returns>
public string SingleTotal()
{
Stopwatch sw1 = new Stopwatch();
sw1.Start();
CountString("single", "http://www.bing.com");
CountString("single", "http://www.360.cn");
CountString("single", "http://www.baidu.com");
sw1.Stop();
return sw1.ElapsedMilliseconds.ToString();
}
多线程并发执行3次函数:
public string MultiTotal()
{
Stopwatch sw2 = new Stopwatch();
sw2.Start();
// 使用Lambda表达式构造 Action,这里可以传多个方法来并行执行,不限于3个
Parallel.Invoke( ()=>CountString("Multi", "http://www.bing.com"),
()=>CountString("Multi", "http://www.360.cn"),
()=>CountString("Multi", "http://www.baidu.com"));
sw2.Stop();
return sw2.ElapsedMilliseconds.ToString();
}
在 OnGet() 函数中分别调用单线程方法和多线程方法:
public string DemoStr; //定义页面取值的变量来显示执行时间
public string ContentStr; //定义页面取值变量来显示函数执行情况
public void OnGet()
{
string time1 = SingleTotal(); //单线程花费时间
string time2 = MultiTotal(); //多线程花费时间
DemoStr = string.Format("单线程花费时间为:{0},多线程花费时间为:{1}", time1, time2);
//用表格来展示数据更清晰
ContentStr = "<table border='1' width='600' style='margin:0 auto;'>" +
"<tr><td>类型</td><td>线程ID</td><td>网址</td><td>响应内容长度</td></tr>" +
ContentStr + "</table>";
}
在 Index.cshtml 中修改代码如下:
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<br />
<p> @Model.DemoStr </p>
<br />
<p> @Html.Raw(Model.ContentStr) </p>
</div>
编译后运行结果如下:
显然,使用多线程时花费的时间更少。
从表格的执行明细中也可以看到:使用多线程的时候3个方法分别是3个不同线程来执行的。