• 编写高质量代码改善C#程序的157个建议——建议71:区分异步和多线程应用场景


    建议71:区分异步和多线程应用场景

    初学者有时候会将异步和多线程混为一谈。如果对它们之间的区别不是很清楚,很容易写出下面这样的代码:

    private void buttonGetPage_Click(object sender, EventArgs e)  
    {  
        Thread t = new Thread(() =>
        {  
            var request = HttpWebRequest.Create("http://www.cnblogs.com");  
            var response = request.GetResponse();  
            var stream = response.GetResponseStream();  
            using (StreamReader reader = new StreamReader(stream))  
            {  
                var content = reader.ReadLine();  
                textBoxPage.Text = content;  
            }  
        });  
        t.Start();  
    } 

    上面的代码模拟了在一个Winform窗体程序中,单击Button获取某个网页的内容并显示出来。可以预见,如果该网页的内容很多,或者当前的网络状况不太好,获取网页的过程会持续较长时间。于是,我们可能会想到用新起工作线程的方法来完成这项工作,这样在等待网页内容返回的过程中Winform界面就不会被阻滞了。

    是的,上面的程序解决了界面阻滞的问题,但是,它高效吗?答案是:不。要理解这一点,需要从“IO操作的DMA(Direct Memory Access)模式”开始讲起。DMA即直接内存访问,是一种不经过CPU而直接进行内存数据存储的数据交换模式。通过DMA的数据交换几乎可以不损耗CPU的资源。在硬件中,硬盘、网卡、声卡、显卡等都有DMA功能。CLR所提供的异步编程模型就是让我们充分利用硬件的DMA功能来释放CPU的压力。
    了解这一点,再来重新审视本建议开头的这个例子。其开头部分的示例代码可以用图6-1来阐述。

    图6-1 多线程工作的示意图


    为了获取网页,CLR新起了一个工作线程,然后在读取网页的整个过程中,该工作线程始终被阻滞,直到获取网页完毕为止。在整个过程中,工作线程被占用着,这意味着系统的资源始终被消耗着、等待着。

    如果我们修改一下代码,使用异步模式去实现,代码如下所示:

    private void buttonGetPage_Click(object sender, EventArgs e)  
    {  
        var request = HttpWebRequest.Create("http://www.cnblogs.com");  
        request.BeginGetResponse(this.AsyncCallbackImpl, request);  
    }  
     
    public void AsyncCallbackImpl(IAsyncResult ar)  
    {  
        WebRequest request = ar.AsyncState as WebRequest;  
        var response = request.EndGetResponse(ar);  
        var stream = response.GetResponseStream();  
        using (StreamReader reader = new StreamReader(stream))  
        {  
            var content = reader.ReadLine();  
            textBoxPage.Text = content;  
        }  
    } 


    以上代码的工作机制可以用图6-2来描述。

    图6-2 异步工作的机制


    经过修改的示例采用了异步模式,它使用线程池进行管理。新起异步操作后,CLR会将工作丢给线程池中的某个工作线程来完成。当开始I/O操作的时候,异步会将工作线程还给线程池,这时候就相当于获取网页的这个工作不会再占用任何CPU资源了。直到异步完成,即获取网页完毕,异步才会通过回调的方式通知线程池,让CLR响应异步完毕。可见,异步模式借助于线程池,极大地节约了CPU的资源。

    明白了异步和多线程的区别后,我们来确定两者的应用场景:

    • 计算密集型工作,采用多线程。
    • IO密集型工作,采用异步机制。

    转自:《编写高质量代码改善C#程序的157个建议》陆敏技

  • 相关阅读:
    element-ui 中 el-table 根据scope.row行数据变化动态显示行内控件
    vue.js 父组件主动获取子组件的数据和方法、子组件主动获取父组件的数据和方法
    把json1赋值给json2,修改json2的属性,json1的属性也一起变化
    win10下当前目录右键添加CMD快捷方式
    element-ui
    vscode 头部注释插件
    IE浏览器new Date()带参返回NaN解决方法
    常用css
    使用DataGridView控件显示数据
    第四章 ADO.NET
  • 原文地址:https://www.cnblogs.com/jesselzj/p/4741910.html
Copyright © 2020-2023  润新知