• 并发系列64章(异步编程)第二章


    前言

    异步编程的概念我在第一章概要的时候,提及了。在此再次简略概要一次。

    它采用future模式或者回调模式机制,以避免产生不必要的线程。
    

    异步编程测试的标准

    在第一个写这个的原因,是因为测试可能比开发重要。因为在开发一个项目的时候呢?有一个自动化高效精准测试,决定了上线是否稳定。因为程序出bug测试出来可以改,方案不行换方案,但是测试不行上线了。这时候面临的问题就比较大,因为这时候产生了数据。

    比如说 app 一张表的设计不合理,在自动化测试中没有体现出来,那么你要更换表的时候就显得异常困难,这时候到底换不换表的结构呢?换了之后,如何兼容之前的版本?迭代的方案是啥。好的,扯得很远了。

    当然我们作为开发人员也要做好单元测试,及子系统测试。好的,近了一点了。

    我们在写一个异步程序的时候,是有3个测试必须通过。

    1.同步成功

    2.异步成功

    3.异步失败

    先介绍一下如何异步测试:

    public static async Task<T> DelayResult<T>(T result, TimeSpan delay)
    {
    	await Task.Delay(delay);
    	return result;
    }
    

    如何测试的时候如果这样写:

    [Fact]
    public async void Test1()
    {
    	TimeSpan timeSpan = new TimeSpan();
    	Program.DelayResult<int>(1, timeSpan);
    }
    

    那么这个测试是有问题的。
    比如:

    public static async Task<T> DelayResult<T>(T result, TimeSpan delay)
    {
    	await Task.Delay(delay);
    	throw new Exception("error");
    	return result;
    }
    

    本来我是应该抛出异常的,但是:

    结果是下面这样的。
    原因就涉及到一个异常捕获的问题了,可以查询一下原理。
    运行测试的时候应该加上await:

    [Fact]
    public async void Test1()
    {
    	TimeSpan timeSpan = new TimeSpan();
    	await Program.DelayResult<int>(1, timeSpan);
    }
    


    那么这个时候就可以捕获到异常。

    下面介绍一些例子。

    指数退避

    这个是什么意思呢?比如说,我们访问我们的一条url的时候,访问失败。
    接下来我们应该做的是重试,那么是否马上重试?不是的,除非是阻塞式的api调用,例如登录。
    但是呢,如果不是阻塞式的,那么应该把资源分配均衡。因为你一次失败,第二次的也有可能失败。
    那么这时候指数退避是一种良好的方法。

    static async Task<string> visitUrl(string url)
    {
    	using (var client = new HttpClient())
    	{
    		var nextDelay = TimeSpan.FromSeconds(1);
    		for (int i = 0; i != 3; ++i)
    		{
    			try
    			{
    				return await client.GetStringAsync(url);
    			}
    			catch
    			{
    
    			}
    			await Task.Delay(nextDelay);
    			nextDelay = nextDelay + nextDelay;
    		}
    		// 返回最后的结果方便得出错误
    		return await client.GetStringAsync(url);
    	}
    }
    

    测试:

    [Fact]
    public async void Test1()
    {
    	await Program.visitUrl("www.xxx.com");
    }
    

    结果:

    测试花了7秒。
    正确验证测试我就不测了。

    实现超时功能

    上面的这个代码,我们发现一个问题啊,如果访问那个链接要好久,那么这也很受伤啊。
    是否能加入一个超时,如果访问一段时间没有返回结果,那么把资源留给别的需求者。

    public static async Task<string> visitTimeoutUrl(HttpClient client,string url)
    {
    	var visitTask=client.GetStringAsync(url);
    	var timeoutTask = Task.Delay(3000);
    	var completedTask = await Task.WhenAny(visitTask,timeoutTask);
    	if (completedTask == timeoutTask)
    	{
    		return null;
    	}
    	return await visitTask;
    }
    

    上文实现了一个简单的超时。
    然后改一下:

    public static async Task<string> visitUrl(string url)
    {
    	using (var client = new HttpClient())
    	{
    		var nextDelay = TimeSpan.FromSeconds(1);
    		for (int i = 0; i != 3; ++i)
    		{
    			try
    			{
    				var result= await visitTimeoutUrl(client,url);
    				if (result != null)
    				{
    					return result;
    				}
    			}
    			catch
    			{
    
    			}
    			await Task.Delay(nextDelay);
    			nextDelay = nextDelay + nextDelay;
    		}
    		// 返回最后的结果方便得出错误
    		return await visitTimeoutUrl(client, url);
    	}
    }
    
  • 相关阅读:
    今天要查一下,如果没有密保手机的号码在使用,怎么更换qq的密保手机
    昨天晚上让妈妈弄了一下机顶盒的安装
    Help Tomisu UVA
    UVA 1363 Joseph's Problem 找规律+推导 给定n,k;求k%[1,n]的和。
    UVA 1640 The Counting Problem UVA1640 求[a,b]或者[b,a]区间内0~9在里面各个数的数位上出现的总次数。
    ZOJ 3962 Seven Segment Display 16进制的八位数加n。求加的过程中所有的花费。显示[0,F]有相应花费。
    ZOJ 3963 Heap Partition set维护。给一个序列,将其划分成尽量少的序列,使每一个序列满足按照顺序构造二叉树,父母的值<=孩子的值。
    Irrelevant Elements UVA
    GCD XOR UVA 12716 找规律 给定一个n,找多少对(a,b)满足1<=b<=a<=n,gcd(a,b)=a^b;
    Colossal Fibonacci Numbers! UVA 11582 寻找循环节
  • 原文地址:https://www.cnblogs.com/mengcheng9300/p/12661740.html
Copyright © 2020-2023  润新知