|
在 C#1 的时候就包含了APM,在 APM 模型中,异步操作通过 IAsyncResult 接口实现,包括两个方法 BeginOperationName 和 EndOperationName ,分别表示开始和结束异步操作。
Demo
我们先来看一个同步示例。新建WPF程序,在界面上放一个按钮。点击按钮访问外网,会有一定时间的阻塞。
private void SyncBtn_Click(object sender, RoutedEventArgs e)
{
// 记录时间
Debug.WriteLine(DateTime.Now.TimeOfDay.ToString() +
",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
// 访问外网网站网站
var req = WebRequest.Create("https://docs.newrelic.com/docs/apm/agents/net-agent/getting-started/net-agent-compatibility-requirements-net-framework/");
req.GetResponse();
// 记录时间
Debug.WriteLine(DateTime.Now.TimeOfDay.ToString() +
",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
}当我们点击按钮后,因为web请求是同步的,会阻塞UI线程一定时间。从输出日志上看阻塞时间是 1 秒钟左右,此时界面呈卡死状态。

日志输出如下:
13:16:09.5031834,ThreadID = 1
13:16:10.5220362,ThreadID = 1从运行效果和日志,我们可以看出:
- • WebRequest方法调用前后都是在同一个线程上执行-UI线程
- • WebReqeust方法阻塞了UI线程,导致“假死”现象
WebRequest也提供了异步方法,BeginGetResponse,EndGetResponse。我们修改一下代码,新增一个按钮。
临时加更干货分享
大家能看到这里,已是对我们的支持了。分享一组11月录制的.NET6零基础教程。我们喜欢做这样的分享,它足够的基础,对新手友好。如果需要的话,就来免费领取吧!

资料免费自取:
由于内容过多不便呈现,需要视频教程和配套源码的小伙伴,点击下方卡片!
也可点击下方卡片:点击后自动复制威芯号,并跳转到威芯。搜索威芯号添加,内容已做打包,备注知乎
即可免费领取,注意查收!
临时加更一个win10激活工具(HEU_KMS_Activator),非常方便的哦,手残党也能秒激活!压缩包密码可以添加上方卡片领取的哦!!!
链接:https://pan.baidu.com/s/1NsEy5aKx3L6spaTo-8U4vg?pwd=5555
提取码:5555这里也是为大家收集到一下.NET开发必备的工具也是方便大家能够快速进行开发。
Octopus 是唯一一款面向 .Net 开发人员的 Microsoft Web 开发人员工具,用于管理软件的发布和部署。
它是一个部署工具,它采用 .net 软件开发人员生成的 .net 编程包,并将它们部署在各种平台上,如(Windows、Linux、Azure)。
链接:https://pan.baidu.com/s/1EbwR9FM19HnneRW57vt_oA?pwd=5555
提取码:5555
前缀是来自 Stackify 的代码分析器工具,它在 http://asp.net 程序员运行他的应用程序时在后台运行。
Microsoft Web 开发人员工具捕获所有请求并提供所有事件的详细报告。
该工具还可以对缓慢的 Web 请求进行故障排除并找到慢速查询。
可以使用 Stackify Prefix 的最佳方法是查看应用程序日志并将正确的日志附加程序(log4net、NLog 或 Serilog)添加到您的项目中。
链接:https://pan.baidu.com/s/1VUs31QVZfL7QwEKp6HQJdg?pwd=5555
提取码:5555
private void APM_Btn_Click(object sender, RoutedEventArgs e)
{
// 记录时间
Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() +
",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
// 访问外网网站网站
var req = WebRequest.Create("https://docs.newrelic.com/docs/apm/agents/net-agent/getting-started/net-agent-compatibility-requirements-net-framework/");
req.BeginGetResponse(new AsyncCallback(t => { WebRequestCallback(t,req); }), null);
// 记录时间
Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() +
",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
}
/// <summary>
/// 异步回调
/// </summary>
/// <param name=&#34;result&#34;></param>
private void WebRequestCallback(IAsyncResult result, WebRequest request)
{
var response = request.EndGetResponse(result);
// 获取返回数据流
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(&#34;2-&#34; + DateTime.Now.TimeOfDay.ToString() +
&#34;,ThreadID = &#34; + Thread.CurrentThread.ManagedThreadId);
}
}运行效果如下:

日志输出如下:
1-13:10:01.7734197,ThreadID = 1
3-13:10:01.8826176,ThreadID = 1
2-13:10:03.2614022,ThreadID = 14从运行效果和日志,我们可以看出:
- • 异步方法不会阻塞调用方法,调用后立刻返回
- • 异步方法会在另外一个线程上执行
IAsyncResult
BeginOperationName 方法会返回一个实现了 IAsyncResult 接口的对象。该对象存储了关于异步操作的信息。

转到定义,我们可以看到接口中都包含哪些内容:

自定义异步方法
实现该接口,定义自己的异步方法。
public class MyWebRequestResult : IAsyncResult
{
/// <summary>
/// 用户定义属性,可以存放数据
/// </summary>
public object? AsyncState => throw new NotImplementedException();
/// <summary>
/// 获取用于等待异步操作完成的 WaitHandle
/// </summary>
public WaitHandle AsyncWaitHandle => throw new NotImplementedException();
/// <summary>
/// 表示异步操作是否是同步完成
/// </summary>
public bool CompletedSynchronously => throw new NotImplementedException();
/// <summary>
/// 表示异步操作是否完成
/// </summary>
public bool IsCompleted => throw new NotImplementedException();
}我们需要新建一个回调函数:
public class MyWebRequestResult : IAsyncResult
{
/// <summary>
/// 异步回调函数
/// </summary>
private AsyncCallback _callback;
public string Result { get; private set; }
// 构造函数
public MyWebRequest(AsyncCallback asyncCallback, object state)
{
_callback = asyncCallback;
}
// 设置结果
public void SetComplete()
{
AsyncState = result;
Result = result;
if(null != _callback)
{
_callback(this);
}
}
// ...
}在这之后就可以自定义 APM 异步模型了:
public IAsyncResult BeginMyWebRequest(AsyncCallback callback)
{
// 1. 先给 IAsyncResult 进行赋值
var myResult = new MyWebRequestResult(callback, null);
var request = WebRequest.Create(&#34;https://docs.newrelic.com/docs/apm/agents/net-agent/getting-started/net-agent-compatibility-requirements-net-framework/&#34;);
// 2. 新建线程,执行耗时任务
new Thread(() => {
using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
var str = sr.ReadToEnd();
// 3. 耗时任务结束后 调用回调函数 & 保存结果
myResult.SetComplete(str);
}
}).Start();
return myResult;
}
public string EndMyWebRequest(IAsyncResult asyncResult)
{
MyWebRequestResult myResult = asyncResult as MyWebRequestResult;
return myResult.Result;
}新增一个按钮,进行调用:
private void MyAPM_Btn_Click(object sender, RoutedEventArgs e)
{
// 记录时间
Debug.WriteLine(&#34;1-&#34; + DateTime.Now.TimeOfDay.ToString() +
&#34;,ThreadID = &#34; + Thread.CurrentThread.ManagedThreadId);
// 调用 Begin 方法
BeginMyWebRequest(new AsyncCallback(MyAPM_Callback));
// 记录时间
Debug.WriteLine(&#34;3-&#34; + DateTime.Now.TimeOfDay.ToString() +
&#34;,ThreadID = &#34; + Thread.CurrentThread.ManagedThreadId);
}
private void MyAPM_Callback(IAsyncResult result)
{
// 从这里可以获得 异步操作的结果
var myResult = result as MyWebRequestResult;
var msg = EndMyWebRequest(myResult);
// 记录时间
Debug.WriteLine(&#34;2-&#34; + DateTime.Now.TimeOfDay.ToString() +
&#34;,ThreadID = &#34; + Thread.CurrentThread.ManagedThreadId);
}运行效果如下:

日志输出如下:
1-14:48:42.7278184,ThreadID = 1
3-14:48:42.7311174,ThreadID = 1
2-14:48:45.1049069,ThreadID = 6结合效果和日志,我们可以得出如下结论:
- • 自定义的异步方法没有导致 UI 卡顿
- • APM就是把耗时的任务交给新线程去做,然后利用委托进行回调
普通方法的异步
如果是普通方法,也可以通过 委托异步(BeginInvoke, EndInvoke):
public void MyAction()
{
var func = new Func<string, string>(t => {
Thread.Sleep(2000);
return t;
});
func.BeginInvoke(&#34;inputStr&#34;, t => {
string result = func.EndInvoke(t);
},null);
}总结
- 1. APM 模型是基于IAsyncResult来实现异步操作的
- 2. 异步操作开始时,把委托传递给 IAsyncResult
- 3. 在新线程上执行耗时操作
- 4. 耗时操作结束后,修改 IAsyncResult 里的结果数据,并调用 IAsyncResult 里的委托回调
- 5. 在回调里获取 异步操作 的结果
原文链接:.NET异步编程模式(二) |
|