zoukankan      html  css  js  c++  java
  • 9多线程与异步

    本篇博客对应视频讲解

    回顾

    上一篇内容讲了如何进行http网络请求。最核心的还是HttpClient类,配合HttpRequestMessageHttpResponseMessage类可以自定义请求内容以及处理返回内容。当然在实际的项目中使用,我们还可以借助其他的类库。不过我们仍然要掌握最基础的用法。

    简说异步

    异步只是一个概念,相对于同步的概念。

    好比操作系统,早期是单用户,之后支持了多用户。支持多用户,可不是说我创建多个用户名,用不同的用户登录这么简单。比如机器A上有两个用户分别为Admin与User。那么我用电脑B使用用户Admin远程登录机器A,你用电脑C使用User用户登录机器A。这样,在机器A上,同时有两个用户登录,这两个用户可以同时进行操作,比如我浏览网页,你观看电影,互不影响。

    对于程序来说,一个进程,代码要按照顺序去执行。这就会造成很多问题。比如我们的程序要下载一个文件,那么在下载完这个文件前(不取消),我们无法进行其他的操作。这个时候我们就要借助线程去处理。我们让下载文件这个操作在另一个线程当中去执行,这样就不会造成当前操作的阻塞。

    关于C#的异步相关概念,可阅读官方文档进一步的理解。里面有几个示例能帮助我们更好理解。 在C#中,为了简化异步的操作,多出了asyncawait关键词,以及Task类等。因为进行多线程编码在之前是比较繁琐和容易出问题的。

    我们还是通过一个简单的示例进行说明。 以下程序需要将项目支持语言设置为最新。才能支持async Main方法。

        static async Task Main(string[] args)
        {
            Console.WriteLine("程序运行");
            //DoSomethings(); //异步执行
            await DoSomethings(); //同步,等待完成
            Console.WriteLine("用户操作");
            Console.ReadLine();
        }
    
        static async Task<string> DoSomethings()
        {
            Console.WriteLine("开始获取数据...");
            // 进行网络请求,通常是费时操作
            using (var wc = new WebClient())
            {
                var result = await wc.DownloadStringTaskAsync("https://www.baidu.com");
                Console.WriteLine("获取成功");
                return "";
            }
        }
    

    以上代码,第4行与第5行分别是两种方式,分别使用这两种方式,观察输出结果的不同。

    多线程

    多线程编程向来是比较繁琐和容易出错的,借助C#中的异步编程,我们可以更好的组织我们的代码。多线程很多作用,最常见的用法:

    1. 防止主线程阻塞。将一些费时操作(如网络请求、计算、I/O读写等)放到非主线程中去操作。我们日常使用的程序基本都有一个主线程(通常是UI线程,用来与用户进行交互)和其他若干线程组成。

    2. 另一种是加快处理速度,比如进行批量操作时,我们可以一个一个来执行,也可以将任务分给多个线程一起执行,这样会大大加快处理速度。

    我们来通过一个示例说明: 我们来批量下载一些图片,比如美女图片、高清壁纸等。

        /// <summary>
        /// 获取图片地址
        /// </summary>
        /// <returns></returns>
        static List<string> GetImageLinks()
        {
            var imageLinks = new List<string>();
            // 下载多个页面内容
            for (int page = 0; page < 5; page++)
            {
                using (var wc = new WebClient())
                {
                    // 获取网页内容
                    var xmlStr = wc.DownloadString("https://bing.ioliu.cn/?p=" + page);
                    // 解析html
                    var doc = new HtmlDocument();
                    doc.LoadHtml(xmlStr);
    
                    // 使用linq获取图片地址
                    var Links = doc.DocumentNode
                        .Descendants("div")
                        .Where(d => d.Attributes["class"].Value == "card progressive")
                        .Select(s =>
                        {
                            var link = s.Element("img").Attributes["src"].Value;
                            return link.Replace("400x240", "1920x1080");
                        })
                        // 去重
                        .Distinct()
                        .ToList();
                    imageLinks.AddRange(Links);
                }
            }
    
            return imageLinks.Distinct().ToList();
        }
    
    
        /// <summary>
        /// 下载图片
        /// </summary>
        /// <param name="link"></param>
        static async Task DownloadImageAsync(string link)
        {
            string fileName = link.Substring(link.LastIndexOf("/") + 1);
            string fullPath = Path.Combine(@"e:\images", fileName);
            using (var wc = new WebClient())
            {
                try
                {
                    await wc.DownloadFileTaskAsync(new Uri(link), fullPath);
                    Console.WriteLine("下载" + fileName + "完成");
                }
                catch (Exception)
                {
    
                    Console.WriteLine("保存出错:" + fullPath);
                }
            }
        }
    

    使用方法:

    	// 先获取图片链接
    	var links = GetImageLinks();
    	// 记录用时
    	var watch = new Stopwatch();
    
    	var tasks = new List<Task>();
    	// 计时开始
    	watch.Start();
    	// 下载图片
    	foreach (var link in links)
    	{
    	tasks.Add(DownloadImageAsync(link));
    	}
    	// 等待所有任务执行完毕
    	Task.WaitAll(tasks.ToArray());
    	watch.Stop();
    	Console.WriteLine("总共用时:" + watch.ElapsedMilliseconds / 1000.0 + "秒");
    	Console.ReadLine();
    
  • 相关阅读:
    分享的选择
    @contextmanager 另外一种实现上下文的方法(含yield 生成器)
    Linux ☞ Good good study,day day up
    Linux常用命令
    python 新手遇到的问题
    Pyqt 基础功能
    redis安装使用
    python 对任意文件(jpg,png,mp3,mp4)base64的编码解码
    PHP7.* AES的加密解密
    Ubuntu宝塔面板设置网站 Apache Server API为Apache 2.0 Handler模式
  • 原文地址:https://www.cnblogs.com/msdeveloper/p/9773847.html
Copyright © 2011-2022 走看看