自己写一个异步webapi,占了一个坑

照着前面文章 http://huqingyu.com/blog/2019/08/1511.htm,自己写了一个异步调用,改了路由,结果总是返回null

然后访问/api/cc/async,就是返回null,实际上它去了Get()方法…
访问/cc/async就正确了,嘿,被路由坑了

ASP.NET sync over async(异步中同步,什么鬼?)

src:https://www.cnblogs.com/xishuai/p/asp-net-sync-over-async.html

async/await 是我们在 ASP.NET 应用程序中,写异步代码最常用的两个关键字,使用它俩,我们不需要考虑太多背后的东西,比如异步的原理等等,如果你的 ASP.NET 应用程序是异步到底的,包含数据库访问异步、网络访问异步、服务调用异步等等,那么恭喜你,你的应用程序是没问题的,但有一种情况是,你的应用程序代码比较老,是同步的,但现在你需要调用异步代码,这该怎么办呢?有人可能会说,很简单啊,不是有个 .Result 吗?但事实真的就这么简单吗?我们来探究下。

首先,放出几篇经典文章:

上面文章的内容,我们后面会说。光看不练假把式,所以,如果真正要体会 sync over async,我们还需要自己动手进行测试:

  • 1. 异步调用使用 .Result,同步调用使用 .Result
  • 2. 异步调用使用 await,同步调用使用 Task.Run
  • 3. 异步调用使用 await,同步调用使用 .Result
  • 4. 异步调用使用 Task.Run,同步调用使用 .Result
  • 5. 异步调用使用 await .ConfigureAwait(true),同步调用使用 .Result
  • 6. 异步调用使用 await .ConfigureAwait(false),同步调用使用 .Result
  • 7. 异步调用使用 await,异步调用使用 await
  • 8. 测试总结

先说明一下,在测试代码中,异步调用使用的是 HttpClient.GetAsync 方法,并且测试请求执行两次,关于具体的分析,后面再进行说明。

1. 异步调用使用 .Result,同步调用使用 .Result

测试代码:

 

输出结果:


 

简单总结:同步代码中调用异步,上面的测试代码应该是我们最常写的,为什么没有出现线程阻塞,页面卡死的情况呢?而且代码中调用了 GetAsync,为什么请求线程只有一个?后面再说,我们接着测试。

2. 异步调用使用 await,同步调用使用 Task.Run

测试代码:

 

输出结果:

简单总结:根据上面的输出结果,我们发现,在一个请求过程中,总共会出现三个线程,一个是开始的请求线程,接着是 Task.Run 创建的一个线程,然后是异步方法中 await 等待的执行线程,需要注意的是,ManagedThreadId1 和 ManagedThreadId4 始终是一样的。

3. 异步调用使用 await,同步调用使用 .Result

测试代码:

 

输出结果:


 

简单总结:首先,页面是卡死状态,ManagedThreadId3 并没有输出,也就是执行到 await client.GetAsync 的时候,线程就阻塞了。

4. 异步调用使用 Task.Run,同步调用使用 .Result

测试代码:

 

输出结果:


 

简单总结:和第三种情况一样,页面也是卡死状态,但不同的是,ManagedThreadId3 是输出的,测试它的主要目的是和第三种情况形成对比,以便了解 HttpClient.GetAsync 中到底是什么鬼?

5. 异步调用使用 await .ConfigureAwait(true),同步调用使用 .Result

测试代码:

 

输出结果:


 

简单总结:和上面两种情况一样,页面也是卡死状态,它的效果和第三种完全一样,ManagedThreadId3 都没有输出的。

6. 异步调用使用 await .ConfigureAwait(false),同步调用使用 .Result

测试代码:

输出结果:


输出结果:

 

简单总结:和第五种情况形成对比,仅仅只是把 ConfigureAwait 参数设置为 false,结果却完全不同。

7. 异步调用使用 await,异步调用使用 await

测试代码:

 

简单总结:注意这是异步的写法,调用和被调用方法都是异步的,从输出的结果中,我们就会发现,这种情况和上面的六种情况,有一个最明显的区别就是,请求线程和结束线程不是同一个,说明什么呢?线程是异步等待的。

8. 测试总结

先梳理一下测试结果:

  1. 异步调用使用 .Result,同步调用使用 .Result:通过,始终一个线程。
  2. 异步调用使用 await,同步调用使用 Task.Run:通过,三个线程,请求开始和结束为相同线程。
  3. 异步调用使用 await,同步调用使用 .Result:卡死,线程阻塞。
  4. 异步调用使用 Task.Run,同步调用使用 .Result:卡死,线程阻塞。
  5. 异步调用使用 await .ConfigureAwait(true),同步调用使用 .Result:卡死,线程阻塞。
  6. 异步调用使用 await .ConfigureAwait(false),同步调用使用 .Result:通过,两个线程,await 执行为单独一个线程。
  7. 异步调用使用 await,异步调用使用 await:通过,两个线程,请求开始和结束为不同线程。

上面这么多的测试情况,看起来可能有些晕,我们先从最简单的第二种情况开始分析下,首先,页面是同步方法,请求线程可以看作是一个主线程 1,然后通过 Task.Run 创建线程 2,让它去执行 Test2 方法,需要注意的是,这时候主线程 1 并不会往下执行(从输出结果可以看出),它会等待线程 2 执行,主要是等待线程 2 执行返回结果,在 Test2 方法中,一切是异步方法,await client.GetAsync 会创建又一个线程 3 去执行,并且线程 2 等待它返回结果,然后最终回到线程 1 上,在整个过程中,虽然有三个线程,但这三个线程并不是同时工作的,而是一个执行之后等待另一个执行的结果,所以整个执行过程还是同步的。

第三种和第二种情况的不同就是,异步调用由 Task.Run 改成了 .Result,然后就造成了页面卡死,在 Don’t Block on Async Code 这篇文章中,就是详细说明的这种情况,为什么会卡死呢?其实你从同样卡死的第四种情况和第五种情况中,可以发现一些线索,ConfigureAwait 的说明是:试图继续回夺取的原始上下文,则为 true;否则为 false。什么意思呢?就是它可以变身为请求线程,最能体现出这一点的是,如果设置为 true,那么在这个线程中,就可以访问 HttpContext.Current,那为什么在同步调用中,设置为 true 就造成页面卡死呢?我们分析一下,页面是同步方法,请求线程可以看作是一个主线程 1,然后调用 Test3 异步方法,这时候主线程 1,会在这里等待异步的执行结果,在 Test3 方法中创建一个线程 2,因为把 ConfigureAwait 设置为了 true,那么线程 2 就想把自己变身成为请求线程(谋权篡位),也就是线程 1,但是人家线程 1 现在正在门口等它呢?线程 2 却想占有线程 1 的地位,很显然,这是不成功的,那什么情况下可以谋权篡位成功呢?就是线程 1 不在,也就是线程 1 回到线程池中了,这就是异步等待的效果,也是它的威力。

针对第三种情况,简单画了一个示意图:

在第五种情况中,因为把 ConfigureAwait 设置为 false,线程 2 不想谋权篡位了,它只想老老实实的做事,把执行结果返回给请求线程 1,那么整个请求执行过程就是顺利的。

同步调用异步测试中,还剩一个第一种情况,它和其他情况不同的是,没有异步方法,只是使用的是 .Result,那为什么它是通过的?并且线程始终是一个呢?首先,页面请求开始,创建一个请求线程 1,因为 Test 方法并不是异步方法,所以还是线程 1 去执行它,执行到了 client.GetAsync 这一步,因为没有使用 await,所以并不会创建一个线程去执行它,并且最终的是,虽然 GetAsync 是异步方法,但再其实现代码中,设置了 ConfigureAwait(false):

所以,整个过程应该是这样的,在测试代码中始终是一个请求线程在执行,并且在 client.GetAsync 的执行中,会创建另外一个线程 2 去执行,然后线程 1 等待线程 2 的执行结果,因为 GetAsync 的实现并不在测试代码中,所以表现出来就是一个线程在执行,虽然是异步方法,但它和同步方法一样,为什么?因为线程始终在等待另一个线程的执行结果,也就是说,在某一时刻,始终是一个线程在执行,其余线程都在等待。

sync over async(异步中同步)是否可行?通过上面的测试结果可以得出是可行的,但要注意一些写法问题:

  • 异步调用使用 .Result,而不能出现 await。
  • 不能出现 ConfigureAwait(true)。
  • 可以使用 Task.Run,但仅限于不返回结果的执行线程。

当然最好的方式是异步到底

java maven checkstyle失败

官网: https://maven.apache.org/plugins/maven-checkstyle-plugin/index.html

checkstyle问题多多,需要一一解决,先来看看可能出现的问题:
Execution verify of goal org.apache.maven.plugins:maven-checkstyle-plugin:2.16 …
Could not find resource ‘etc/config/checkstyle.xml’
Could not find resource ‘etc/config/checkstyle-suppressions.xml’

它说没找到这两个文件,那么贴2个上去,在项目下建立这3个文件:

checkstyle.xml
etc/config/checkstyle-verify.xml

checkstyle-suppressions.xml

 

特别说明:官网上这个是错的 https://maven.apache.org/plugins/maven-checkstyle-plugin/examples/suppressions-filter.html
里面的https://checkstyle.org/dtds/suppressions_1_0.dtd早就没了

然后在pom.xml的<build>里面加上:注意是build

好,这样能解决大部分问题了。

emcc编译选项

source: https://emscripten.org/docs/compiling/Building-Projects.html

To see a list of all available ports
emcc
 –show-ports

一、使用SDL_image

参考【1】用这个可以获得image宽、高,返回image data
Gets preloaded image data and the size of the image.

二、使用SDL_net
SDL_net has also been added to ports, use it with

三、编译优化
In order to properly optimize code, it is usually best to use the same optimization flags and other compiler options when compiling source to object code, and object code to JavaScript (or HTML).

 

参考:
1. emscripten.h
https://emscripten.org/docs/api_reference/emscripten.h.html#c.emscripten_get_preloaded_image_data

wasm

emcc client.c -s USE_SDL=2 -s USE_SDL_NET=2 -s USE_SDL_IMAGE=2 -s WASM=1 -o sdl2.html

 

client.c:
—————————–
#include “SDL.h”
#include “SDL_net.h”
#include <emscripten/emscripten.h>
int EMSCRIPTEN_KEEPALIVE linkToServer(int port)
{

}

How to reduce rpm size using rpmbuild

编译出来的rpm就是比deb大,就去研究如何减少rpm的大小
关键还是在SPECS文件上,要改变默认的压缩方式,在%prep后面加上这句就行了:
%define _binary_payload w7.xzdio
# Compression type and level for source/binary package payloads.
# “w9.gzdio”: gzip level 9 (default).
# “w9.bzdio”: bzip2 level 9.
# “w7.xzdio”: xz level 7, xz’s default.
# “w7.lzdio”: lzma-alone level 7, lzma’s default

 

参考:
https://stackoverflow.com/questions/9292243/rpmbuild-change-compression-format

[win10 linux subsystem] dpkg-deb error: control directory has bad permissions 777 (must be >=0755 and <=0775)

打算用win10 linux 子系统来打包ubuntu使用的deb安装包,结果运行dpkg-deb报错:
control directory has bad permissions 777 (must be >=0755 and <=0775)

解决方案:
sudo umount /mnt/e
sudo mount -t drvfs -o uid=1000,gid=1000,umask=022 e: /mnt/e

就是说要重新mount一下,mount的时候就决定了目录权限

[2019-07-06 updated]
按照http://zuyunfei.com/2018/06/15/file-system-configuration-in-wsl/的方法,
在C:\Users\%CurrentUser%\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\rootfs\etc上建立一个文件,名字叫wsl.conf,内容如下:
[automount]
enabled = true
root = /mnt/
options = “uid=1000,gid=1000,metadata,umask=022”
mountFsTab = false

进入任一目录,打开linux shell,再mount看一下,完美~

 

参考:
https://www.askingbox.com/question/error-message-dpkg-deb-error-control-directory-has-bad-permissions-777
https://www.askingbox.com/question/linux-get-own-user-id-uid-gid
http://zuyunfei.com/2018/06/15/file-system-configuration-in-wsl/

 

测试MQ算术编码

JBig2_fcd14492.pdf
H.2 Test sequence for arithmetic coder
The source data:
0x00, 0x02, 0x00, 0x51, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x52, 0x87, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA,
0x82, 0xC0, 0x20, 0x00, 0xFC, 0xD7, 0x9E, 0xF6, 0xBF, 0x7F, 0xED, 0x90, 0x4F, 0x46, 0xA3, 0xBF

The encoded data:
0x84, 0xC7, 0x3B, 0xFC, 0xE1, 0xA1, 0x43, 0x04, 0x02, 0x20, 0x00, 0x00, 0x41, 0x0D, 0xBB, 0x86,
0xF4, 0x31, 0x7F, 0xFF, 0x88, 0xFF, 0x37, 0x47, 0x1A, 0xDB, 0x6A, 0xDF, 0xFF, 0xAC

 

MIUI v7 6.2.18 广告域名屏蔽

使用范围:

  • 日历的农历旁边的广告
  • 下载管理器推荐
  • 搜索的热门搜索

使用方法:

  • 然后把hosts部分代码追加在/etc/hosts文件后面
  • 飞行模式
  • 日历、下载管理器和搜索 清除数据(设置——其他应用管理——全部)

以下是hosts部分

其中最后一行是屏蔽日历中农历旁边的广告的,但是误伤了日历订阅的图片,如果介意的话可以删除最后一行
至于天气的广告是可以关闭的

 

 

另外的参考:

MAC下编译动态链接库函数的可见性

编译的时候加上 -fvisibility=hidden,可以隐藏一般函数的可见性,如果要使得函数可见,则要加上__attribute__((visibility(“default”)))

代码如下:

 

保存成a.c,然后用命令行编译:

gcc -shared -fPIC a.c -fvisibility=hidden

此时生成a.out,然后用nm查看,可以看到函数a前面是小t,说明是隐藏的,函数b前面是大T,说明是可见的