并发系列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);
	}
}

未完

今天写博客的时候,一直出现error,就先到这吧。

下一章,还是几个例子感受一下。以上为个人理解,如有不对望请指出。

原文地址:https://www.cnblogs.com/aoximin/p/12622060.html

时间: 2024-10-10 08:15:39

并发系列64章(异步编程)第二章的相关文章

C++ 编程第二章小结

switch()用法的注意事项 1:switch语句中的表达式只能是整形数据,字符型数据和枚举型数据,case后面的产量表达式的类型必须与switch括号后面的类型相匹配 2:各个case(包括default)的出现次序可以任意,每个case在带有break的前提下,case的次序不影响执行结果 循环设计 循环设计的几种分类方法这里介绍几种 1:字符图形类 2:素数判断类 3:逻辑判断类 4:级数逼近类 a:对于图形类的基本循环格式一般是 for(int i = 1; i < 10 ;i ++)

《Java并发变成实践》读书笔记---第二章 线程安全性

什么是线程安全性 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问.从非正式的意义上来说,对象的状态是指存储在状态变量(例如实例或静态域)中的数据."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化.所以编写线程安全的代码更侧重于如何防止在数据上发生不受控的并发访问. 如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误

sql系列(基础篇)-第二章 约束和排序数据

更好的看↑代码点击VIEW PLAN 第二章 约束和排序数据 1. 在 emp 表中选择工资介于 1500 到 2500 的员工的信息: 注意:使用 between 下边界 and 上边界时,条件包括边界值: [email protected]>l 1 select * from emp 2* where sal between 1500 and 2500 [email protected]>/ EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ----

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

前言 是在第二章基础上续写的,主要是完结第二章例子部分. 请看下面几个例子,感受一下. 报告进度 不管我们完任何app,每次更新的时候都能看到进度条. 而我们知道ui界面更新,一般来说是和更新程序异步的,但是更新程序又要通知ui进度. 代码: public class Program { static double percentComplete = 0; static void Main(string[] args) { doit(); Console.ReadKey(); } public

[原创译书] JS函数式编程 第二章总结

?? Functional Programming in Javascript 主目录第二章 函数式编程基础上一节 函数式程序员的工具集 第二章总结 为了理解函数式编程,这章覆盖了很大范围的主题.首先我们分析了一个编程语言的函数式是什么意思, 并且评估了Javascript函数式编程能力.接下来,我们用Javascript实现了一些函数式编程的核心概念, 并展示了一些Javascript内建的函数式编程函数. 尽管Javascript有一些函数式编程的工具,它函数式编程核心的大部分仍被隐藏着,并

【读书笔记】C#高级编程 第十三章 异步编程

(一)异步编程的重要性 使用异步编程,方法调用是在后台运行(通常在线程或任务的帮助下),并不会阻塞调用线程.有3中不同的异步编程模式:异步模式.基于事件的异步模式和新增加的基于任务的异步模式(TAP,可利用async和await关键字来实现). (二)异步模式 1.C#1的APM 异步编程模型(Asynchronous Programming Model). 2.C#2的EAP 基于事件的异步模式(Event-based Asynchronous Pattern). 3.TAP 基于任务的异步模

C#本质论读书笔记:第一章 C#概述|第二章 数据类型

第一章 1.字符串是不可变的:所有string类型的数据,都不可变,也可以说是不可修改的,不能修改变量最初引用的数据,只能对其重新赋值,让其指向内存中的一个新位置. 第二章 2.1 预定义类型或基本类型: C#语言的基本类型包括8种整数类型,2种用于科学计算的二级制浮点类型,1种用于金融计算的十进制浮点类型,1种布尔类型以及一种字符类型. 2.1.1 整数类型: 要注意int32的范围,如果是要保存10位以上的数字编号的时候,要么使用string,要么使用int64,因为int32只能保存到10

读高性能JavaScript编程 第二章 让我知道了代码为什么要这样写

代码为什么要这样写? function initUI(){ var doc = document, bd = doc.body, links = doc.getElementsByTagName_r("a"), i = 0, len = links.length; while(i < len){ update(links[i++]); } doc.getElementById("go-btn").onclick = function(){ start(); }

C++模板编程 - 第二章 - 函数模板

模板被编译两次 书上说模板被编译两次:一次是实例化之前,检查模板代码本身,这个好理解,就是检查和模板没有关系的代码:第二次编译是在实例化的时候,看看把类型带入进去有没有问题,比如说字符串没有除法. 还有一个需要注意的问题:模板在进行实例化的时候,编译器要能够看到模板的定义.下面的代码会挂掉,因为main.cpp把max.h包含进来,但是其中没有max的实现 1 // max.h 2 template<typename T> 3 max..... 4 5 // max.cpp 6 #includ