早在C#1的时候就有了APM。虽然不是很熟悉,但是多少还是见过的。就是那些类是BeginXXX和EndXXX的方法,且BeginXXX返回值是IAsyncResult接口。
//1、同步方法
private void button1_Click(object sender, EventArgs e)
{
Debug.WriteLine("【Debug】线程ID:" + Thread.CurrentThread.ManagedThreadId);
var request = WebRequest.Create("https://github.com/");//为了更好的演示效果,我们使用网速比较慢的外网
request.GetResponse();//发送请求
Debug.WriteLine("【Debug】线程ID:" + Thread.CurrentThread.ManagedThreadId);
label1.Text = "执行完毕!";
}
【说明】为了更好的演示异步效果,这里我们使用winform程序来做示例。(因为winform始终都需要UI线程渲染界面,如果被UI线程占用则会出现“假死”状态)
【效果图】
private void button2_Click(object sender, EventArgs e)
{
//1、APM 异步编程模型,Asynchronous Programming Model
//C#1[基于IAsyncResult接口实现BeginXXX和EndXXX的方法]
Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
var request = WebRequest.Create("https://github.com/");
request.BeginGetResponse(new AsyncCallback(t =>//执行完成后的回调
{
var response = request.EndGetResponse(t);
var stream = response.GetResponseStream();//获取返回数据流
using (StreamReader reader = new StreamReader(stream))
{
StringBuilder sb = new StringBuilder();
while (!reader.EndOfStream)
{
var content = reader.ReadLine();
sb.Append(content);
}
Debug.WriteLine("【Debug】" + sb.ToString().Trim().Substring(0, 100) + "...");//只取返回内容的前100个字符
Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
label1.Invoke((Action)(() => { label1.Text = "执行完毕!"; }));//这里跨线程访问UI需要做处理
}
}), null);
Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
【效果图】
public class MyWebRequest : IAsyncResult
{
public object AsyncState
{
get { throw new NotImplementedException(); }
}
public WaitHandle AsyncWaitHandle
{
get { throw new NotImplementedException(); }
}
public bool CompletedSynchronously
{
get { throw new NotImplementedException(); }
}
public bool IsCompleted
{
get { throw new NotImplementedException(); }
}
}
这样肯定是不能用的,起码也得有个存回调函数的属性吧,下面我们稍微改造下:
public IAsyncResult MyBeginXX(AsyncCallback callback)
{
var asyncResult = new MyWebRequest(callback, null);
var request = WebRequest.Create("https://github.com/");
new Thread(() => //重新启用一个线程
{
using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
var str = sr.ReadToEnd();
asyncResult.SetComplete(str);//设置异步结果
}
}).Start();
return asyncResult;//返回一个IAsyncResult
}
public string MyEndXX(IAsyncResult asyncResult)
{
MyWebRequest result = asyncResult as MyWebRequest;
return result.Result;
}
调用如下:
private void button4_Click(object sender, EventArgs e)
{
Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
MyBeginXX(new AsyncCallback(t =>
{
var result = MyEndXX(t);
Debug.WriteLine("【Debug】" + result.Trim().Substring(0, 100) + "...");
Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
}));
Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
效果图:
while (!asyncResult.IsCompleted)//循环,直到异步执行完成 (轮询方式)
{
Thread.Sleep(100);
}
var stream2 = request.EndGetResponse(asyncResult).GetResponseStream();
或
asyncResult.AsyncWaitHandle.WaitOne();//阻止线程,直到异步完成 (阻塞等待)
var stream2 = request.EndGetResponse(asyncResult).GetResponseStream();
补充:如果是普通方法,我们也可以通过委托异步:(BeginInvoke、EndInvoke)
public void MyAction()
{
var func = new Func<string, string>(t =>
{
Thread.Sleep(2000);
return "name:" + t + DateTime.Now.ToString();
});
var asyncResult = func.BeginInvoke("张三", t =>
{
string str = func.EndInvoke(t);
Debug.WriteLine(str);
}, null);
}
EAP
EAP 基于事件的异步模式,Event-based Asynchronous Pattern
此模式在C#2的时候随之而来。
先来看个EAP的例子:
private void button3_Click(object sender, EventArgs e)
{
Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler((s1, s2) =>
{
Thread.Sleep(2000);
Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
});//注册事件来实现异步
worker.RunWorkerAsync(this);
Debug.WriteLine("【Debug】主线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
【效果图】(同样不会阻塞UI界面)
TAP
TAP 基于任务的异步模式,Task-based Asynchronous Pattern
到目前为止,我们觉得上面的APM、EAP异步模式好用吗?好像没有发现什么问题。再仔细想想...如果我们有多个异步方法需要按先后顺序执行,并且需要(在主进程)得到所有返回值。
首先定义三个委托:
public Func<string, string> func1()
{
return new Func<string, string>(t =>
{
Thread.Sleep(2000);
return "name:" + t;
});
}
public Func<string, string> func2()
{
return new Func<string, string>(t =>
{
Thread.Sleep(2000);
return "age:" + t;
});
}
public Func<string, string> func3()
{
return new Func<string, string>(t =>
{
Thread.Sleep(2000);
return "sex:" + t;
});
}
然后按照一定顺序执行:
public void MyAction()
{
string str1 = string.Empty, str2 = string.Empty, str3 = string.Empty;
IAsyncResult asyncResult1 = null, asyncResult2 = null, asyncResult3 = null;
asyncResult1 = func1().BeginInvoke("张三", t =>
{
str1 = func1().EndInvoke(t);
Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
asyncResult2 = func2().BeginInvoke("26", a =>
{
str2 = func2().EndInvoke(a);
Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
asyncResult3 = func3().BeginInvoke("男", s =>
{
str3 = func3().EndInvoke(s);
Debug.WriteLine("【Debug】异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
}, null);
}, null);
}, null);
asyncResult1.AsyncWaitHandle.WaitOne();
asyncResult2.AsyncWaitHandle.WaitOne();
asyncResult3.AsyncWaitHandle.WaitOne();
Debug.WriteLine(str1 + str2 + str3);
}
除了难看、难读一点好像也没什么 。不过真的是这样吗?
var task1 = Task<string>.Run(() =>
{
Thread.Sleep(1500);
Console.WriteLine("【Debug】task1 线程ID:" + Thread.CurrentThread.ManagedThreadId);
return "张三";
});
//其他逻辑
task1.Wait();
var value = task1.Result;//获取返回值
Console.WriteLine("【Debug】主 线程ID:" + Thread.CurrentThread.ManagedThreadId);