C#之同步、异步、多线程
今天我们来一起学习C#中的同步。异步以及多线程。在学习这些之前,我们先来简单地了解下进程和线程之间的关系,一个应用程序即一个进程,而一个进程的执行需要多个线程来辅助,所以运行一个进程后,会运行多个线程。所谓的同步,即按照代码的顺序执行,也就是用同一个线程来执行所有的操作。而在执行异步操作时,会启动多个线程来执行各个操作,并且他们开启的顺序以及执行的时间各不相同,具体的区别我们在代码中进行比较。
我们新建一个基于.Net 4.0的网站,并添加一个页面,并在页面中添加U两个按钮,分别用来执行同步操作和异步操作。
1 同步、异步对比
同步非诚容易理解,我们通常执行的代码就是用同步来操作了,即用主线程来按顺序执行所有操作,但异步就不同了,对于不同的操作,是通过开启一个新线程来执行的。
在C#中,异步是基于委托来实现的。然后通过委托的BeginInvoke()
l来开启一个新线程,然后用这个新线程来执行这个委托中方法。
该方法需要三个参数,第一个是委托方法需要的参数,第二个是该线程执行完成后的回调方法,它是一个AsyncCallback类型的委托,第三个参数是一个状态值。该方法返回一个IAsyncResult类型的变量,该变量可用于执行EndInvoke()参数
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" Text="同步" OnClick="Button1_Click"/>
<asp:Button ID="Button2" runat="server" Text="异步" OnClick="Button2_Click" />
<asp:Button ID="Button3" runat="server" Text="Task" OnClick="Button3_Click" />
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;
using System.Threading.Tasks;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Response.Write("同步异步");
}
}
public long DoSomething(string name)
{
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff") + name + "开始执行DoSomething 当前线程Id:" + Thread.CurrentThread.ManagedThreadId + "<br/>");
long result = 0;
for (int i = 0; i < 100000000; i++)
result += i;
Thread.Sleep(2);
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff") + name + "结束执行DoSomething 当前线程Id:" + Thread.CurrentThread.ManagedThreadId + "结算结果" + result + "<br/>");
return result;
}
//同步
protected void Button1_Click(object sender, EventArgs e)
{
Response.Write("*********************************************************************************************************************<br/>");
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff ") + "开始执行同步 当前线程" + Thread.CurrentThread.ManagedThreadId + "<br/>");
for (int i = 0; i < 5; i++)
{
string name = string.Format("同步_{0}", i);
//同步调用 用主线程去处理该操作 同步运行速度较慢 不占用过多的系统资源 同步按顺序执行
DoSomething(name); //无返回值的
}
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff ") + "结束执行同步 当前线程" + Thread.CurrentThread.ManagedThreadId + "<br/>");
Response.Write("*********************************************************************************************************************<br/>");
}
//异步
protected void Button2_Click(object sender, EventArgs e)
{
Response.Write("*********************************************************************************************************************<br/>");
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff ") + "开始执行异步 当前线程" + Thread.CurrentThread.ManagedThreadId + "<br/>");
// Action<string> method = DoSomething;//无返回值
Func<string, long> method = DoSomething; //有返回值
AsyncCallback callback = (result) =>
{
Response.Write("执行callback " + result.AsyncState + "<br/>"); //实例化回调方法的委托
// long iResult = method.EndInvoke(result); //每个线程只能有一个EndInvoke 卡主子线程
};
for (int i = 0; i < 5; i++)
{
string name = string.Format("异步_{0}", i);
// doSomethingMethod.Invoke(name);
IAsyncResult iResult = method.BeginInvoke(name, callback, "abc"); //异步调用 启动多个线程 运行速度快 占用更多的系统资源 异步执行顺序是随机的
//线程的请求分配是随机的 运行同一任务的时间也不同
// long result = method.EndInvoke(iResult); //获取异步调用的返回值 //会卡主线程 等待异步完成
//while(! iResult.IsCompleted)
//{
// Response.Write("请等待 ..............");
// Thread.Sleep(100); //主线程等待
//}
}
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff ") + "结束执行异步 当前线程" + Thread.CurrentThread.ManagedThreadId + "<br/>");
Response.Write("*********************************************************************************************************************<br/>");
}
}
2 Task
在C#中启动新线程有两种方法,第一个是通过委托的BeginInvoke方法来实现,第二个就是通过TaskFactory类来实现,通过该类对象的StartNew()方法来启动一个新线程,该方法需要一个Action委托类型的参数,标识该线程要执行的参数,且该方法返回一个Task类的对象。其中TaskFactory类的ContinueWhenAll()可用于在某些线程执行完成后要执行的操作,该方法需要一个Task[]类型的参数,包含了所有要监视的进程。
//Task
protected void Button3_Click(object sender, EventArgs e)
{
Response.Write("*********************************************************************************************************************<br/>");
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff ") + "开始执行操作 当前线程" + Thread.CurrentThread.ManagedThreadId + "<br/>");
Response.Write("接项目<br/>");
Response.Write("谈价格<br/>");
Response.Write("需求分析<br/>");
Response.Write("定金<br/>");
Action action1 = () => Coding("Jack", "Portal"); //实例化几个用于多线程的委托
Action action2 = () => Coding("Bob", "WeiChat");
Action action3 = () => Coding("Andt", "BackOffice");
TaskFactory taskFactory = new TaskFactory(); //实例化一个多线程
List<Task> taskList = new List<Task>();
taskList.Add(taskFactory.StartNew(action1)); //创建并执行多个线程
taskList.Add(taskFactory.StartNew(action2));
taskList.Add(taskFactory.StartNew(action3));
//之前的多线程完成后 启动一个新线程
Task taskMerge = taskFactory.ContinueWhenAll(taskList.ToArray(), (tList) =>
{
Response.Write("合成 线程id: " + Thread.CurrentThread.ManagedThreadId + "<br/>");
});
//等待某个线程结束
Task.WaitAny(taskList.ToArray());
Response.Write("测试<br/>");
Response.Write("收20%<br/>");
// taskList.Add(taskMerge);
//等执行合并操作到的线程结束
Task.WaitAll(new Task[] {taskMerge});
Response.Write("验收<br/>");
Response.Write("收钱<br/>");
Response.Write(DateTime.Now.ToString("hh:mm:ss.fff ") + "结束执行操作 当前线程" + Thread.CurrentThread.ManagedThreadId + "<br/>");
Response.Write("*********************************************************************************************************************<br/>");
}