分類
發燒車訊

西班牙東北部化學工廠爆炸 釀1死6傷

摘錄自2020年1月15日中央社報導

西班牙東北部發生化學工廠爆炸事故,造成一人喪命,傷及六人。當地媒體引述消防人員的說法表示,爆炸很有可能是化學意外所引起。

「先鋒報」(La Vanguardia)和「國家報」(ElPais)等媒體在網站上表示,這起在塔拉戈納省(Tarragona)發生的爆炸引發衝擊波,造成一棟建築物坍塌,一人身亡。

加泰隆尼亞救災服務處發言人證實,附近有建築物倒塌並有一人喪生,但還無法確定大樓倒塌與爆炸或大火有關。

民防局建議附近民眾待在室內,並預防性緊鎖門窗,但說:「沒有證據顯示(爆炸)產生有毒的煙霾。」

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

中國向美國買稀土 美中貿易協議內容引議論

摘錄自2020年1月16日中央社報導

中美16日達成第一階段貿易協議,協議中驚現中國向美國購買稀土的承諾。由於中國是全球最大的稀土生產國,貿易戰中也曾宣稱以限制稀土出口反制美國,這項承諾立即引起外界議論。

在中美第一階段貿易協議中,中國承諾未來兩年向美國購買2000億美元產品。路透社報導,其中包括鈧和釔等兩種用於生產燈具和電腦的稀土。稀土曾被視為中國的「殺手鐧」,如今中國卻向美國購買,箇中原因引起各方議論。

此外,報導也指出,美國國防部近幾個月已採取行動,為美國的稀土、稀土磁鐵項目提供資金,目的是要建立一個沒有中國的「國內供應鍊」。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

.NET進階篇06-async異步、thread多線程2

知識需要不斷積累、總結和沉澱,思考和寫作是成長的催化劑

內容目錄

一、線程Thread

.NET中線程操作封裝為了Thread類,可以讓開發者對線程進行直觀操作。Thread提供了實例方法用於管理線程的生命周期和靜態方法用於控制線程的一些訪問存儲等一些外在的屬性,相當於工作空間環境變量了

1、生命周期

線程的生命周期有創建、啟動、可能掛起、等待、恢復、異常、然後結束。用Thread類可以容易控制一個線程的全生命周期

Thread類的構造函數重載可以接受ThreadStart無參數和ParameterizedThreadStart有參數的委託,然後調用實例的Start()方法啟動線程。Thread的構造函數的帶有參數的委託,參數是一個object類型,因為我們可以傳入任何信息

Thread t1 = new Thread(() => {
    Console.WriteLine($"新線程  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});
t1.Start();
Thread t2 = new Thread((obj) => {
    Console.WriteLine($"新線程  {Thread.CurrentThread.ManagedThreadId.ToString("00")},參數 {obj.ToString()}");
});
t2.Start("hello kitty");

線程啟動后,可以調用線程的Suspend()掛起線程,線程就會處於休眠狀態(不繼續執行線程內代碼),調用Resume()喚醒線程,還有一個不太建議使用的Abort()通過拋出異常的方式來銷毀線程,隨後線程的狀態就會變為AbortRequested

常用的還有線程的等待,在主線程上啟用工作線程后,有時需要等待工作線程的完成后,主線程才繼續工作。可以調用實例方法Join(),當然我們可以傳入時間參數來表明我主線程最多等你多久

2、後台線程

上一章我們知道Thread默認創建的是前台線程,前台線程會阻止系統進程的退出,就是啟動之後一定要完成任務的後台線程會伴隨着進程的退出而退出。通過設置屬性IsBackground=true改為後台線程。另外還可以通過設置Priority指定線程的優先級。但這個並不總會如你所想設置了高優先級就一定最先執行。操作系統會優化調度,這也是線程不太好控制的原因之一

3、靜態方法

上面介紹的都是Tread的實例方法,Thread還有一些常用靜態方法。有時線程設置不當,會有些意想不到的的bug

1.線程本地存儲

AllocateDataSlot和AllocateNamedDataSlot用於給所有線程分配一個數據槽。像下面例子所示,如果不在子線程中給數據槽中放入數據,是獲取不到其他線程往裡面放的數據。

var slot= Thread.AllocateNamedDataSlot("testSlot");
//Thread.FreeNamedDataSlot("testSlot");
Thread.SetData(slot, "hello kitty");
Thread t1 = new Thread(() => {
    //Thread.SetData(slot, "hello kitty");
    var obj = Thread.GetData(slot);
    Console.WriteLine($"子線程:{obj}");//obj沒有值
});
t1.Start();

var obj2 = Thread.GetData(slot);
Console.WriteLine($"主線程:{obj2}");

在聲明數據槽的時候.NET提醒我們如果要更好的性能,請使用ThreadStaticAttribute標記字段。什麼意思?我們來看下面這個例子

//
// 摘要:
//     在所有線程上分配未命名的數據槽。 為了獲得更好的性能,請改用以 System.ThreadStaticAttribute 特性標記的字段。
//
// 返回結果:
//     所有線程上已分配的命名數據槽。
public static LocalDataStoreSlot AllocateDataSlot();

例子中的如果不在靜態字段上標記ThreadStatic輸出結果就會一致。ThreadStatic標記指示各線程的靜態字段值是否唯一

[ThreadStatic]
static string name = string.Empty;
public void Function()
{
    name = "kitty";
    Thread t1 = new Thread(() => {
        Console.WriteLine($"子線程:{name}");//輸出空
    });
    t1.Start();
    Console.WriteLine($"主線程:{name}");//輸出kitty
}

還有一個ThreadLocal提供線程數據的本地存儲,用法和上面一樣,在每個線程中聲明數據僅供自己使用

ThreadLocal<string> local = new ThreadLocal<string>() { };
local.Value = "hello kitty";
Thread t = new Thread(() => {
    Console.WriteLine($"子線程:{local.Value}");
});
t.Start();
Console.WriteLine($"主線程:{local.Value}");

上面的靜態方法用於線程的本地存儲TLS(Thread Local Storage),Thread.Sleep方法在開發調試時也是經常用的,讓線程掛起指定的時間來模擬耗時操作

2.內存柵欄

先說一個常識問題,為什麼我們發布版本時候要用Release發布?Release更小更快,做了很多優化,但優化對我們是透明的(計算機里透明認為是個黑盒子,內部邏輯細節對我們不開放,和生活中透明意味着完全掌握了解不欺瞞剛好相反),一般優化不會影響程序的運行,我們先借用網上的一個例子

bool isStop = false;
Thread t = new Thread(() => {
    bool isSuccess = false;
    while (!isStop)
    {
        isSuccess = !isStop;
    }
});
t.Start();
Thread.Sleep(1000);
isStop = true;
t.Join();
Console.WriteLine("主線程執行結束");

上面例子如果在debug下能正確執行完直到輸出“主程序執行結束”,然而在release下卻一直會等待子線程的完成。這裏子線程中isStop一直為false。首先這是一個由多線程共享變量引起的問題,所以我們建議最好的解決辦法就是盡量不共享變量,其次可以使用Thread.MemoryBarrier和VolatileRead/Write以及其他鎖機制犧牲一點性能來換取數據的安全。(上面例子測試如果在子線程while中進行Console.writeLine操作,奇怪的發現release下也能正常輸出了,猜測應該是進行了內存數據的更新)

release優化會將t線程中的isStop變量的值加載到CPU Cache中,而主線程修改了isStop值在內存中,所以子線程拿不到更新后的值,造成數據不一致。那麼解決辦法就是取值時從內存中獲取。Thread.MemoryBarrier()就可以讓在此方法之前的內存寫入都及時的從CPU Cache中更新到內存中,在此之後的內存讀取都要從內存中獲取,而不是CPU Cache。在例子中的while內增加Thread.MemoryBarrier()就能避免數據不一致問題。VolatileRead/Write是對MemoryBarrier的分開解釋,從處理器讀取,從處理器寫入。

4、返回值

前面聲明線程時,可以傳遞參數,那麼想要有返回值該如何去做呢?Thread並沒有提供返回值的操作,後面.NET給出的對Thead的高級封裝給出了解決方案,直接使用即可。那目前我們使用thread類就要自己實現下帶有返回值的線程操作,都是通過委託實現的,這裏簡單介紹一種,(共享外部變量也是可以,不建議)

private Func<T> ThreadWithReturn<T>(Func<T> func)
{
    T t = default(T);
    Thread thread = new Thread(() =>
    {
        t = func.Invoke();
    });
    thread.Start();
    return () =>

    {
        thread.Join();
        return t;
    };
}
//調用
Func<intfunc = this.ThreadWithReturn<int>(() =>
{
    Thread.Sleep(2000);
    return DateTime.Now.Millisecond;
});
int iResult = func.Invoke();

二、線程池ThreadPool

.NET起初提供Thread線程類,功能很豐富,API也很多,所以使用起來比較困難,況且線程還不都是很像理想中運行,所以從2.0開始提供了ThreadPool線程池靜態類,全是靜態方法,隱藏了諸多Thread的接口,讓線程使用起來更輕鬆。線程池可用於執行任務、發送工作項、處理異步 I/O、代表其他線程等待以及處理計時器

1、工作隊列

常用ThreadPool線程池靜態方法QueueUserWorkItem用於將方法排入線程池隊列中執行,如果線程池中有閑置線程就會執行,QueueUserWorkItem方法的參數可以指定一個回調函數委託並且傳入參數,像下面這樣

ThreadPool.QueueUserWorkItem((obj) => {
                Console.WriteLine($"線程池中線程  {Thread.CurrentThread.ManagedThreadId.ToString("00")} ,傳入 {obj.ToString()}");
            },"hello kitty");

2、工作線程和IO線程

一般異步任務的執行,不涉及到網絡文件等IO操作的,計算密集型,開發者來調用。而IO線程一般用在文件網絡上,是CLR調用的,開發者無需管。工作線程發起文件訪問調用,由驅動器完成后通知IO線程,IO線程則執行異步任務的回調函數

獲取和設置最小最大的工作線程和IO線程

ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
ThreadPool.SetMaxThreads(1616);
ThreadPool.SetMinThreads(88);

3、和Thread區別

如果計算機只有8個核,同時可以有8個任務運行。現在我們有10個任務需要運行,用Thread就需要創建10個線程,用ThreadPool可能只需要利用8個線程就行,節約了空間和時間。線程池中的線程默認先啟動最小線程數量的線程,然後根據需要增減數量。線程池使用起來簡單,但也有一些限制,線程池中的線程都是後台線程,不能設置優先級,常用於耗時較短的任務。線程池中線程也可以阻塞等待,利用ManualResetEvent去通知,但一般不會使用。

4、定時器

.NET中有很多可以實現定時器的功能,在ThreadPool中,我們可以利用RegisterWaitForSingleObject來註冊一個指定時間的委託等待。像下面這樣,將每隔一秒就輸出消息

ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(true), new WaitOrTimerCallback((obj, b) =>
{
    Console.WriteLine($"obj={obj},tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}");
}),"hello kitty",1000,false);

我們平常見過比較多的還是timer類,timer類在.net內是好幾個地方都有的,在System.Threading、
System.Timer、System.Windows.Form、System.Web.UI等裏面都有Timer,後面都是在第一個System.Threading里的Timer擴展

System.Threading.Timer timer = new System.Threading.Timer((obj) =>
{
    Console.WriteLine($"obj={obj},tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}");
},"hello kitty",1000,1000);

timer的底層有一個TimerQueue,利用ThreadPool.UnsafeQueueUserWorkItem來完成定時功能,和上面我們使用的ThreadPool定時器有一點區別

實際開發中,簡單定時timer就夠用,但一般業務場景比較複雜,需要定製個性化的定時器,比如每月幾號執行,每月第幾個星期幾,幾點執行,工作日執行等。因此我們使用Quarz.NET定時框架,後面框架整合時會用到,用起來也是很簡單的

先就啰嗦這兩點吧,下一篇應該是Task、Parallel以及Async/Await,然後總結介紹下C#的線程模式、線程同步鎖機制、異常處理,線程取消,線程安全集合和常見的線程問題

天長水闊,見字如面,隨緣更新,拜了個拜~

可關注主頁公號獲取更多哈

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

玩轉VSCode-完整構建VSCode開發調試環境

隨着VSCode的不斷完善和強大,是時候將部分開發遷移到VS Code中了。

目前使用VS2019開發.NET Core應用,一直有一個想法,在VS Code中復刻VS的開發環境,同時遷移到VS Code。

那麼現在就開始吧。

首先,安裝最新版的VS Code:,安裝完成后可能會提示升級,升級即可,升級后的版本信息:

版本: 1.40.1 (system setup)
提交: 8795a9889db74563ddd43eb0a897a2384129a619
日期: 2019-11-13T16:49:35.976Z
Electron: 6.1.2
Chrome: 76.0.3809.146
Node.js: 12.4.0
V8: 7.6.303.31-electron.0
OS: Windows_NT x64 10.0.16299

接下來的操作分為幾個步驟:

1. 安裝各種強大VS Code插件

2. 創建.NET Core解決方案和工程

3. 調試運行

好的,那我們開始吧。

一、安裝各種強大的VS Code插件

1. C# extension for Visual Studio Code

這個插件最重要的功能:

  • Lightweight development tools for .
  • Great C# editing support, including Syntax Highlighting, IntelliSense, Go to Definition, Find All References, etc.
  • Debugging support for .NET Core (CoreCLR). NOTE: Mono debugging is not supported. Desktop CLR debugging has .
  • Support for project.json and csproj projects on Windows, macOS and Linux.

2. C# Extensions

這個插件最有用的功能是可以右鍵新建C#類和C#接口,同時支持各種code snippets,例如 ctor 、prop等,具體功能特性,可以查看插件的說明。

 3. Auto-Using for C#

這個插件自動添加using引用。

4. vscode-solution-explorer

這個插件給VS Code增加了解決方案tab, 支持新建解決方案、新建工程、添加引用、Nuget包,這個插件非常有用

Adds a Solution Explorer panel where you can find a Visual Studio Solution File Explorer.

  • Can load any .sln version

  • Supports csproj, vcxproj, fsproj and vbproj (from vs2017 and before)

  • Supports dotnet core projects

  • You can create, delete, rename or move project folders and files.

  • You can create, delete, rename or move solution, solution folders and projects.

  • You can add or remove packages and references when the project is of kind CPS (dotnet core).

 

5. Code Runner(韓俊老師出品,必屬精品)

Run code snippet or code file for multiple languages: C, C++, Java, JavaScript, PHP, Python, Perl, Perl 6, Ruby, Go, Lua, Groovy, PowerShell, BAT/CMD, BASH/SH, F# Script, F# (.NET Core), C# Script, C# (.NET Core), VBScript, TypeScript, CoffeeScript, Scala, Swift, Julia, Crystal, OCaml Script, R, AppleScript, Elixir, Visual Basic .NET, Clojure, Haxe, Objective-C, Rust, Racket, Scheme, AutoHotkey, AutoIt, Kotlin, Dart, Free Pascal, Haskell, Nim, D, Lisp, Kit, and custom command

即選中一段代碼,直接run

6. vscode-icons

通過這個插件,給各個文件和文件夾一個你更熟悉的圖標

7. Visual Studio IntelliCode

VS代碼智能提示,根據上下文語境,自動推薦你下一步用到的代碼,後台基於AI的

8. NuGet Package Manager

Nuget包管理,快速查詢定位Nuget包,並安裝。不過嘗試了一下午自定義Nuget源,沒搞定,估計是URL不對

9. Docker

10. Kubernetes

其他的還需要配置GitHub、TFS類似的源代碼管理,TFS搞了兩個插件,都不好使,後續搞定后再更新一次。

 

二、創建.NET Core解決方案和工程

此時,VS Code的環境基本配置差不多了,接下來有兩種模式,創建解決方案和工程。

1. 通過vscode-solution-explorer

解決方案有了,很熟悉的感覺。

我們可以繼續創建工程:右鍵sln,Add new project:

此時會彈出工程模板,此時我們選擇ASP.NET Core Web API工程

選擇C#

然後繼續輸入工程名稱:例如 TestWebApi

熟悉的感覺來了。此時就可以開始coding了。

以上是我們通過vscode-solution-explorer新建解決方案和工程。同時我們可以通過命令行來搞定。

2. 通過Dotnet CLI命令行

新建sln:

dotnet "new" "sln" "-n" "EricTest" "-o" "e:\Work\ServiceDependency"

新建ASP.NET Core WebAPI工程

dotnet "new" "webapi" "-lang" "C#" "-n" "TestWebApi" "-o" "TestWebApi"

將TestWebApi工程添加到解決方案EricTest

dotnet "sln" "e:\Work\ServiceDependency\EricTest.sln" "add" "e:\Work\ServiceDependency\TestWebApi\TestWebApi.csproj"

三、調試運行

在Debug選項卡中新增調試配置,重點設置要調試的program

保存后,啟動調試:

 

程序中增加斷點,然後

輸入URL:https://localhost:5001/WeatherForecast

 既可以調試了。

 

以上是今天集中配置VS Code開發調試環境的總結,分享給大家。

 

周國慶

2019//11/16

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

快速搭建Jenkins集群

關於Jenkins集群

在Jenkins上同時執行多個任務時,單機性能可能達到瓶頸,使用Jenkins集群可以有效的解決此問題,讓多台機器同時處理這些任務可以將壓力分散,對單機版Jenkins的單點故障的隱患也有分散作用,今天就來實戰快速搭建Jenkins集群,Jenkins版本是2.190.2;

如何做到快速搭建集群

通過Docker可以省去大部分準備工作,您只需在Linux電腦上安裝docker,在輔以少量命令和操作即可完成集群搭建;

環境信息

本次實戰的環境一共要用三台電腦,它們的設置都是一樣的,如下:

  1. 操作系統:CentOS Linux release 7.6.1810
  2. 防火牆關閉
  3. docker:1.13.1

三台電腦的信息如下:
| 主機名 | IP地址 | 作用 |
|–|–|–|
| master | 192.168.133.131 | Jenkins集群的master節點,提供web服務 |
| agent1 | 192.168.133.132 | Jenkins集群的一號工作接節點,標籤是maven |
| agent2 | 192.168.133.133 | Jenkins集群的二號工作接節點,標籤是gradle |

建議agent2節點的內存大於4G,因為下一篇的實戰操作會用agent2編譯構建spring-framework,對內存的需求略大;

準備工作

  1. 後面的所有操作都是root賬號;
  2. 在每台電腦上創建文件夾/usr/local/jenkins

    創建Jenkins的master

  3. 登錄master機器,執行以下命令:
docker run \
  -u root \
  -idt \
  --name master \
  -p 8080:8080 \
  -p 50000:50000 \
  -v /usr/local/jenkins:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkinsci/blueocean:1.19.0
  1. 執行docker logs master,會在控制台显示jenkins的登錄秘鑰,如下圖紅框所示:
  2. 瀏覽器輸入地址: ,显示Jenkins登錄頁面,如下圖所示,在紅框位置輸入剛才複製的登錄秘鑰即可登錄:
  3. 選擇安裝推薦的插件
  4. 靜候插件在線安裝完成:
  5. 接下來是創建管理員和使用實例url的操作,這裏就不多說了,您按實際情況自行斟酌;

    至此,Jenkins的master已經搭建好,接下來將agent1和agent2作為工作節點加入集群;

    加入agent1

  6. 在Jenkins網頁上新增節點,操作如下圖,先進入節點管理頁面:
  7. 如下圖,新增一個節點,名為agent1
  8. 接下來的節點詳情信息如下圖,注意四個紅框中的內容要和圖中保持一致:
  9. 保存成功後會显示機器列表,如下圖,圖標上的紅叉表示機器不在線(此時agent1還沒有接入),點擊紅框:
  10. 如下圖所示,紅框中的命令就是agent1的啟動命令,執行該命令的機器會以agent1的身份加入集群:
  11. 注意上圖紅框中的agent.jar是個名為agent.jar的文件的下載鏈接,將此文件下載到agent1電腦的/usr/local/jenkins目錄下;
  12. ssh登錄agent1電腦,執行以下命令,即可將agent1加入Jenkins集群:
docker run \
  -u root \
  -idt \
  --name agent \
  -v /usr/local/jenkins:/usr/local/jenkins \
  bolingcavalry/openjdk-with-sshpass:8u232 \
  java -jar /usr/local/jenkins/agent.jar \
  -jnlpUrl http://192.168.133.131:8080/computer/agent1/slave-agent.jnlp \
  -secret 44c3e8d1531754b8655b53294bbde6dd99b3aaa91a250092d0d3425534ae1058 \
  -workDir "/usr/local/jenkins"

上述命令中的後半部分,即java -jar ……就是前面圖片紅框中的agent1啟動命令,唯一要改變的是將agent.jar改成絕對路徑/usr/local/jenkins/agent.jar

  1. 上述命令的鏡像是bolingcavalry/openjdk-with-sshpass:8u232,其Dockerfile內容如下,可見非常簡單,就是OpenJDK鏡像裏面安裝了sshpass,這樣的容器可以在執行ssh命令時帶上遠程機器的密碼,而不用等待用戶輸入密碼,這樣便於shell腳本執行ssh命令:
FROM openjdk:8u232

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install --assume-yes sshpass
  1. 去Jenkins的網頁上查看節點列表,如下圖,可見agent1已經成功加入:

    加入agent2

    agent2加入集群的方式和agent1大部分是一樣的,只有以下兩點要注意:

  2. 在Jenkins頁面上創建節點,名稱是agent2
  3. agent2的標籤是gradle,如下圖紅框所示:
  4. 此時agent2也加入成功:

    至此,Jenkins集群搭建完成,這兩個節點帶有不同的標籤,下一篇文章中,我們在這個集群環境創建pipeline任務,並通過標籤被分配到不同的節點上,實現多節點并行執行;

    歡迎關注公眾號:程序員欣宸

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

微信作弊,為3款小遊戲引擎開掛增速,將給小遊戲帶來怎樣的利好?

11月14日小遊戲開發圈子,有一條重磅新聞:“微信小遊戲聯合遊戲引擎廠商,推出引擎插件功能,可為小遊戲提升0.5~2秒的啟動時間”。

引擎插件是個什麼東西?

昨天有不少人在問曉衡:“引擎插件到底是個什麼東西?”、“又要讓我學習新東西嗎?”、“引擎插件是怎麼加速的,不太明白?” …

曉衡也在第一時間,將文檔通讀了一遍,並用自己的小遊戲工程做了測試,對微信小遊戲引擎插件算是有了一個簡單的認識,看下圖:

普通模式,每一個使用遊戲引擎開發的小遊戲,都需要下載遊戲引擎代碼模塊。

引擎插件模式,僅第一個遊戲需要下載引擎代碼,其它使用同類引擎的遊戲,可共享之前 A 遊戲下載過的遊戲引擎代碼,從而加速遊戲的啟動時間。

從事小遊戲開發和運營的夥伴應該都很了解,H5、小遊戲注重啟動加載速度,它對新用戶的體驗和流失都至關重要。

啟動概況分析

估計有人會覺得引擎插件就加快了0.5~2秒有什麼用?眨個眼的時間而已。

其實對使用 Cocos Creator 開發的休閑類的小遊戲來說,目前的微信小遊戲啟動速度已經很不錯了,首包含引擎的話,iOS 在4 ~ 6秒啟動,Android 大多可以在6~8秒左右打開首屏,並不像微信吹噓的1秒啟動,估計只有 引擎和資源全放子包的遊戲可以做到!

下面我將自己個人開發的一款微信小遊戲,在微信公測的前後两天做了一個數據統計,想窺視一下啟動性能對留存的影響,下圖是曉衡的遊戲在8月7日 ~ 8月9日時的活躍情況:

遊戲是在8月7日的晚上10:00點打開的微信公測,微信平台在24小時內持續導入5184的用戶,當天遊戲啟動8121次。不過圖片上的數據比較尷尬,公測一過就沒幾個玩家了,但它不是我們要講的重點,我們是用這個時間節點、用戶數量,來看微信小遊戲的啟動性能表現。

iOS啟動概況

Android啟動概況

從圖中看,8月7日這天 iOS 的總啟動時間比 Android 快 3.88 秒,Android 的用戶流失比 iOS 要多 12.55%,這裏重點也不是說 iOS 和 Android 系統那個好,而是看遊戲的啟動時間對首屏打開留存的影響。

這是另一個朋友的遊戲《周車勞盾》在9月14日微信公測4800+用戶,遊戲啟動7000+次,下面是它在9月13~9月15日的啟動概況:

《周車勞盾》的 iOS 首屏打開留存率由於用戶數太少,不太好與 Android 對比,並且朋友說當時遊戲沒有做分包優化。在9月14日公測當天,由於新用戶多,iOS、Android 的啟動速度都不快,在 10 秒左右。從中也可以看出微信小遊戲用戶,以及微信導量用戶,以 Android 屬性為主。

啟動流失分析

下圖是曉衡的遊戲在8月8日公測時的 Android 手機用戶流失分佈情況,統計一共有 893 名流失用戶:

從前面的啟動概況看到,小遊戲啟動進入首屏是在8.38s,我們以9秒為分界線,將上圖分成左右兩部分:

  • 右邊標註綠色線框,是已經進入遊戲后流失的人數,這部分的優化需要美術和策劃同學的幫助。
  • 左邊紅色線框中的用戶,是在遊戲啟動過程中流失掉的共計679人佔76%,而且前4秒流失的最多共543占 60%,如果不計算已經打開首屏的更是高達80%,因此前幾秒它才是我們關心的重點。

曉衡根據平常使用微信的習慣,模擬分析一下前8秒的用戶是大概會是什麼情況走失的,需要注意的一個前題是,這些用戶都是微信導量進入的,絕大多是手滑不小心點到廣告,並不是目標用戶。

  • 第1秒:1秒流失用戶,手滑的機率最大,似乎經過專業訓練,眼、腦、手的速度都非常的快,遊戲是什麼都沒看清就閃人了;
  • 第2秒:2秒流失用戶,與1秒戶大概差不多,只是動作稍慢而已,此時遊戲圖標已經進入視覺系統,但估計比較模糊,瞬間閃人;
  • 第3秒:3秒流失用戶,不僅遊戲圖標已經從視覺系統進入大腦,遊戲名字估計也是能看清楚,但是沒有任何感覺,同樣是條件反射,快速點擊關閉;
  • 第4秒:4秒流失用戶,已經是把遊戲圖標、名字已經完全進入大腦神經迴路,給他反饋的信號是沒有愛,甚至是反感,迅速閃人了。第4秒很關鍵,因為用戶已經有了思考!
  • 第5~6秒:5~6秒流失用戶,認真看完遊戲圖標、名字,以及加載進度,經過大腦綜合反饋,這個遊戲不值得等待,88了!
  • 第7~8秒:7~8秒流失用戶,估計是盯到了遊戲的加載進度,在100%或某個数字上停止下一瞬間,實在是不耐煩了,什麼個鬼遊戲,半天進不去,走了!

以上分析是曉衡的個人YY,僅供參考,這裏要說的是前 3 秒流失的用戶大多是條件反射,很難轉化。當用戶將遊戲圖標、遊戲名稱看清了后,大腦產生了思考,再離開的這對我們來說還有機會爭取,讓他們早點看到遊戲首屏,已經花了這5、6秒了,體驗一下再走!

提升遊戲0.5 ~ 2秒的啟動速度是非常具有價值的,而且小遊戲絕大多數又是 Android 用戶,特別是對需要買量的遊戲來說,時間就是金錢,毫秒必爭。

引擎插件帶來的好處

下面我們再來看看,引擎插件具體在那些場景下會帶來比較明顯的性能提供,盡可能充分利用這個機制呢?

微信公測

對於個人開發者,使用微信公測功能免費送5000流量,一定要利用好這個機會。將首包資源做到盡量小巧,引擎裁剪、圖片壓縮一定要做足,同時盡量選擇使用量較多的引擎版本號(目前曉衡了解到的,使用較多 Cocos Creator 引擎版本號分佈在:2.0.8 ~ 2.0.10、2.1.3、2.2.0,不過還是要以微信或 Cocos 官方統計為準),這樣容易蹭上已經下載過的遊戲引擎,這對大多數遊戲來說都是適用的。

中重度遊戲

中重度遊戲,通常會依賴較多的引擎模塊,比如 RPG 遊戲中的:地圖、角色動畫,會使用TileMap、Spine、DragonBones、Animation 等模塊,還有一些遊戲會使用到物理引擎模塊、碰撞模塊等,完整引擎模塊高達 1.6M。

隨着微信引擎插件的廣泛普及,以後構建遊戲完全時可以將引擎裁剪到最精簡狀態,大概在550K左右。甚至可以想像到,以後小遊戲平台完全不用上傳引擎代碼,構建時只用配置上使用的什麼引擎,引擎版本號即可。

中重度遊戲利用引擎插件同樣可以快速進入首屏,首包僅保留炫麗的動效和初始界面,用分治的方式動態下載遊戲當前必要的內容,儘快讓用戶參与到遊戲中去。還有隨着 5G 的到來,中重度遊戲的遊戲資源下載劣勢也會得到改善,對小遊戲更是一件好事。

遊戲矩陣

單款小遊戲一般是很難有收益的,甚至是虧本買賣。微信平台,一個小遊戲可以支持10個遊戲的跳轉,目前絕大多數遊戲商廠,都會在小遊戲中集成其它遊戲的入口加大流量,優質的遊戲還會出售遊戲跳轉坑位,有的還價格不菲。個人開發者也意識到了遊戲間跳轉帶來的爆光機會,不少開發者會在微信公眾時,組織邀請好友建立鏈接。

如果是自家開發的休閑小遊戲,利用引擎插件的啟動增速,再配合上自定義的啟動背景(頭條支持),讓玩家感受不出是在不同遊戲中切換,在矩陣中瞬間穿梭,這也將極大增加遊戲的曝光率,降低流失。

小結

劉潤老師說的好:“一切的商業價值,要看是否讓用戶獲益”。

微信引擎插件不僅讓普通用戶能獲得更好的遊戲體驗,也能讓遊戲開發商能中從獲益。點開即玩的小遊戲,縮短了遊戲產品呈現在用戶手中的時間,極大優化了產品的傳遞價值。

曉衡是一個搬運工,傳遞有價值的遊戲開發技術,如果覺得本文對你有用,感謝來看個再看或傳遞給朋友。感謝您的閱讀,願我們在前進的道路上“砥礪前行,共同成長!”

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

賓士傳將開發四款純電動車

世界級車商賓士(Mercedes-Benz)被報導正在開發全新的電動車平台,並將以此平台為基礎,推出四款純電動汽車。

ETToday引述外媒《Car Magazine》的報導,表示賓士正以MRA平台為基礎開發一款名為「EVA」的電動車平台,可供後續開發C-Class到S-Class尺碼的各式車款,最高能搭載400公斤的電池組。而在動力方面,後驅車型可能搭載一具402匹馬力的電動馬達,四驅車行可能還會再加上一具120匹馬力或者201匹馬力的馬達。主動懸載、扭力分配、動能回收等功能也將面面俱到。

該報導表示,賓士官方並未證實這項消息,但將在2018年推出C-Class與E-Class之間的電動轎車,並以此為基礎再行打造一款休旅車。此後還有S-Class和GLS等級的電動車款會問世。《Car Magazine》表示,賓士目標為每年銷售2萬輛電動車,入門車款售價為7萬英鎊起跳。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

2016年將推出的那些新能源汽車政策

2016年新能源汽車仍將高速增長,但還存在很多不確定因素,互聯網企業造車、電池企業准入、低速電動車企業能否借機“轉正”等懸念是否能揭曉?2016年,中國還將推出哪些新能源汽車政策?讓我們先來預測一下。

《電動汽車動力蓄電池回收利用技術政策》

狀態:徵求意見

預發佈時間:2016年初

2015年9月11日,國家發展改革委環資司、工業和資訊化部裝備司聯合發佈《電動汽車動力蓄電池回收利用技術政策》(徵求意見稿)。根據徵求意見稿要求,國家將推動建立統一的動力蓄電池產品編碼制度。動力蓄電池生產企業應對所生產(或進口)的所有動力蓄電池產 品進行編碼,並建立追溯系統追蹤動力蓄電池流向。編碼應與電池產品及整車具有惟一對應性,編碼應標識在動力蓄電池產品顯著位置,且具有較高的牢固性。
 

《乘用車企業平均燃料消耗量管理辦法(草稿)》

狀態:草稿階段

預發佈時間:2016年

據悉,《乘用車企業平均燃料消耗量管理辦法(草稿)》已經制定完成,即將徵求意見。草案中最重要的內容是企業 平均燃料消耗量積分轉結及交易制度,不達標企業將有巨額罰款。根據草稿,乘用車企業平均燃料消耗量不達標罰款納入預算內,作為汽車行業節能管理專項資金, 其中40%用於汽車節能管理,60%用於支援企業為改善燃料經濟性而進行的產品研發。

《電動汽車充電介面及通信協定5項國家標準》

實施時間:2016年1月1日

2015年12月28日,質檢總局、國家標準委聯合國家能源局、工信部、科技部在京發佈新修訂的電動汽車 充電介面及通信協定5項國家標準。在交流充電部分,禁止採用存在安全隱患的直通電纜加普通家用插頭的連接方式;針對大於16安培的充電方式強制要求在車輛 插座和供電插座安裝電子鎖和溫度感測器,防止帶電拉弧及過熱引起的設備損壞和火災等事故……

《低速電動汽車管理辦法》

狀態:未發佈

預發佈時間:2016年

工信部、發改委、公安部、環境部等部門已經對低速電動汽車進行調研,統一的“低速電動汽車管理辦法”有望出 台。據瞭解,在山東,僅德州一個地級市,低速電動汽車的年產量超過10萬輛,低速電動車製造行業和配套產業直接為德州市貢獻超過50億元的GDP,提供了 一大批工作崗位。全國低速電動汽車主要分佈于山東、河北、河南等省,湖北、四川、湖南、安徽、江西、山西等省份隨著市場的發展產銷量也在不斷攀升。

《私人用戶居住地充電基礎設施建設管理示範文本(操作指南)》

狀態:徵求意見

預發佈時間:2016年

2015年10月12日,國家能源局在江蘇常州舉辦了“電動汽車充電基礎設施促進聯盟成立暨建設經驗交流現場 會”,會上發佈了《私人用戶居住地充電基礎設施建設管理示範文本(操作指南)》(徵求意見稿)。該意見稿對具備固定車位和不具備固定車位的私人用戶需準備 的材料及建設流程做出了詳細規定,此外對配電容量不足情況、消防驗收方面也有具體要求,具體細節還需進一步調研討論。

《關於“十三五”新能源汽車充電設施獎勵政策及加強新能源汽車推廣應用的通知(徵求意見稿)》

狀態:徵求意見

預發佈時間:2016年上半年

12月15日,財政部、科技部、工業和資訊化部、發展改革委、國家能源局聯合發佈《關於“十三五”新能源汽車 充電設施獎勵政策及加強新能源汽車推廣應用的通知(徵求意見稿)》。徵求意見稿明確了拿到獎勵的門檻,例如:大氣污染治理重點區域和重點省市(包括北京、 上海、天津、河北、山西、江蘇、浙江、山東、廣東、海南),2016~2020年度新能源汽車(標準車,下同)推廣數量分別不低於3萬輛、3.5萬輛、 4.3萬輛、5.5萬輛、7萬輛,且推廣的新能源汽車數量占本地區新增及更新的汽車總量比例不低於3%、4%、5%、8%、10%。此外,還明確了新能源標準車折算關係,例如純電動乘用車(續駛里程(參配、圖片、詢價) ≥150km)與標準車折算比例為1:1等。
 

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

.NET Core 3.0 單元測試與 Asp.Net Core 3.0 集成測試

單元測試與集成測試

測試必要性說明

相信大家在看到單元測試與集成測試這個標題時,會有很多感慨,我們無數次的在實踐中提到要做單元測試、集成測試,但是大多數項目都沒有做或者僅建了項目文件。這裡有客觀原因,已經接近交付日期了,我們沒時間做白盒測試了。也有主觀原因,面對業務複雜的代碼我們不知道如何入手做單元測試,不如就留給黑盒測試吧。但是,當我們的代碼無法進行單元測試的時候,往往就是代碼開始散發出壞味道的時候。長此以往,將欠下技術債務。在實踐過程中,技術債務常常會存在,關鍵在於何時償還,如何償還。

上圖說明了隨着時間的推移開發/維護難度的變化。

測試框架選擇

在 .NET Core 中,提供了 xUnit 、NUnit 、 MSTest 三種單元測試框架。

MSTest UNnit xUnit 說明 提示
[TestMethod] [Test] [Fact] 標記一個測試方法
[TestClass] [TestFixture] n/a 標記一個 Class 為測試類,xUnit 不需要標記特性,它將查找程序集下所有 Public 的類
[ExpectedException] [ExpectedException] Assert.Throws 或者 Record.Exception xUnit 去掉了 ExpectedException 特性,支持 Assert.Throws
[TestInitialize] [SetUp] Constructor 我們認為使用 [SetUp] 通常來說不好。但是,你可以實現一個無參構造器直接替換 [SetUp]。 有時我們會在多個測試方法中用到相同的變量,熟悉重構的我們會提取公共變量,並在構造器中初始化。但是,這裏我要強調的是:在測試中,不要提取公共變量,這會破壞每個測試用例的隔離性以及單一職責原則。
[TestCleanup] [TearDown] IDisposable.Dispose 我們認為使用 [TearDown] 通常來說不好。但是你可以實現 IDisposable.Dispose 以替換。 [TearDown] 和 [SetUp] 通常成對出現,在 [SetUp] 中初始化一些變量,則在 [TearDown] 中銷毀這些變量。
[ClassInitialize] [TestFixtureSetUp] IClassFixture< T > 共用前置類 這裏 IClassFixture< T > 替換了 IUseFixture< T > ,
[ClassCleanup] [TestFixtureTearDown] IClassFixture< T > 共用後置類 同上
[Ignore] [Ignore] [Fact(Skip=”reason”)] 在 [Fact] 特性中設置 Skip 參數以臨時跳過測試
[Timeout] [Timeout] [Fact(Timeout=n)] 在 [Fact] 特性中設置一個 Timeout 參數,當允許時間太長時引起測試失敗。注意,xUnit 的單位時毫秒。
[DataSource] n/a [Theory], [XxxData] Theory(數據驅動測試),表示執行相同代碼,但具有不同輸入參數的測試套件 這個特性可以幫助我們少寫很多代碼。

以上寫了 MSTest 、UNnit 、 xUnit 的特性以及比較,可以看出 xUnit 在使用上相對其它兩個框架來說提供更多的便利性。但是這裏最終實現還是看個人習慣以選擇。

單元測試

  1. 新建單元測試項目

  2. 新建 Class

  3. 添加測試方法

            /// <summary>
            /// 添加地址
            /// </summary>
            /// <returns></returns>
            [Fact]
            public async Task Add_Address_ReturnZero()
            {
                DbContextOptions<AddressContext> options = new DbContextOptionsBuilder<AddressContext>().UseInMemoryDatabase("Add_Address_Database").Options;
                var addressContext = new AddressContext(options);
    
                var createAddress = new AddressCreateDto
                {
                    City = "昆明",
                    County = "五華區",
                    Province = "雲南省"
                };
                var stubAddressRepository = new Mock<IRepository<Domain.Address>>();
                var stubProvinceRepository = new Mock<IRepository<Province>>();
                var addressUnitOfWork = new AddressUnitOfWork<AddressContext>(addressContext);
    
                var stubAddressService = new AddressServiceImpl.AddressServiceImpl(stubAddressRepository.Object, stubProvinceRepository.Object, addressUnitOfWork);
                await stubAddressService.CreateAddressAsync(createAddress);
                int addressAmountActual = await addressContext.Addresses.CountAsync();
                Assert.Equal(1, addressAmountActual);
            }
    • 測試方法的名字包含了測試目的、測試場景以及預期行為。
    • UseInMemoryDatabase 指明使用內存數據庫。
    • 創建 createAddress 對象。
    • 創建 Stub 。在單元測試中常常會提到幾個概念 Stub , Mock 和 Fake ,那麼在應用中我們該如何選擇呢?
      • Fake – Fake 通常被用於描述 Mock 或 Stub ,如何判斷它是 Stub 還是 Mock 依賴於使用上下文,換句話說,Fake 即是 Stub 也是 Mock 。
      • Stub – Stub 是系統中現有依賴項的可控替代品。通過使用 Stub ,你可以不用處理依賴直接測試你的代碼。默認情況下, 偽造對象以stub 開頭。
      • Mock – Mock 對象是系統中的偽造對象,它決定單元測試是否通過或失敗。Mock 會以 Fake 開頭,直到被斷言為止。
    • Moq4 ,使用 Moq4 模擬我們在項目中依賴對象。
  4. 打開視圖 -> 測試資源管理器。

  5. 點擊運行,得到測試結果。

  6. 至此,一個單元測試結束。

集成測試

集成測試確保應用的組件功能在包含應用的基礎支持下是正確的,例如:數據庫、文件系統、網絡等。

  1. 新建集成測試項目。

  2. 添加工具類 Utilities 。

    using System.Collections.Generic;
    using AddressEFRepository;
    
    namespace Address.IntegrationTest
    {
        public static class Utilities
        {
            public static void InitializeDbForTests(AddressContext db)
            {
                List<Domain.Address> addresses = GetSeedingAddresses();
                db.Addresses.AddRange(addresses);
                db.SaveChanges();
            }
    
            public static void ReinitializeDbForTests(AddressContext db)
            {
                db.Addresses.RemoveRange(db.Addresses);
                InitializeDbForTests(db);
            }
    
            public static List<Domain.Address> GetSeedingAddresses()
            {
                return new List<Domain.Address>
                {
                    new Domain.Address
                    {
                        City = "貴陽",
                        County = "測試縣",
                        Province = "貴州省"
                    },
                    new Domain.Address
                    {
                        City = "昆明市",
                        County = "武定縣",
                        Province = "雲南省"
                    },
                    new Domain.Address
                    {
                        City = "昆明市",
                        County = "五華區",
                        Province = "雲南省"
                    }
                };
            }
        }
    }
  3. 添加 CustomWebApplicationFactory 類,

     using System;
     using System.IO;
     using System.Linq;
     using AddressEFRepository;
     using Microsoft.AspNetCore.Hosting;
     using Microsoft.AspNetCore.Mvc.Testing;
     using Microsoft.EntityFrameworkCore;
     using Microsoft.Extensions.Configuration;
     using Microsoft.Extensions.DependencyInjection;
     using Microsoft.Extensions.Logging;
    
     namespace Address.IntegrationTest
     {
         public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
         {
             protected override void ConfigureWebHost(IWebHostBuilder builder)
             {
                 string projectDir = Directory.GetCurrentDirectory();
                 string configPath = Path.Combine(projectDir, "appsettings.json");
                 builder.ConfigureAppConfiguration((context, conf) =>
                 {
                     conf.AddJsonFile(configPath);
                 });
    
                 builder.ConfigureServices(services =>
                 {
                     ServiceDescriptor descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AddressContext>));
    
                     if (descriptor != null)
                     {
                         services.Remove(descriptor);
                     }
    
                     services.AddDbContextPool<AddressContext>((options, context) =>
                     {
                         //var configuration = options.GetRequiredService<IConfiguration>();
                         //string connectionString = configuration.GetConnectionString("TestAddressDb");
                         //context.UseMySql(connectionString);
                         context.UseInMemoryDatabase("InMemoryDbForTesting");
    
                     });
    
                     // Build the service provider.
                     ServiceProvider sp = services.BuildServiceProvider();
                     // Create a scope to obtain a reference to the database
                     // context (ApplicationDbContext).
                     using IServiceScope scope = sp.CreateScope();
                     IServiceProvider scopedServices = scope.ServiceProvider;
                     var db = scopedServices.GetRequiredService<AddressContext>();
                     var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
    
                     // Ensure the database is created.
                     db.Database.EnsureCreated();
    
                     try
                     {
                         // Seed the database with test data.
                         Utilities.ReinitializeDbForTests(db);
                     }
                     catch (Exception ex)
                     {
                         logger.LogError(ex, "An error occurred seeding the " + "database with test messages. Error: {Message}", ex.Message);
                     }
                 });
             }
         }
     }
    • 這裏為什麼要添加 CustomWebApplicationFactory 呢?
      WebApplicationFactory 是用於在內存中引導應用程序進行端到端功能測試的工廠。通過引入自定義 CustomWebApplicationFactory 類重寫 ConfigureWebHost 方法,我們可以重寫我們在 StartUp 中定義的內容,換句話說我們可以在測試環境中使用正式環境的配置,同時可以重寫,例如:數據庫配置,數據初始化等等。
    • 如何準備測試數據?
      我們可以使用數據種子的方式加入數據,數據種子可以針對每個集成測試做數據準備。
    • 除了內存數據庫,還可以使用其他數據庫進行測試嗎?
      可以。
  4. 添加集成測試 AddressControllerIntegrationTest 類。

     using System.Collections.Generic;
     using System.Linq;
     using System.Net.Http;
     using System.Threading.Tasks;
     using Address.Api;
     using Microsoft.AspNetCore.Mvc.Testing;
     using Newtonsoft.Json;
     using Xunit;
    
     namespace Address.IntegrationTest
     {
         public class AddressControllerIntegrationTest : IClassFixture<CustomWebApplicationFactory<Startup>>
         {
             public AddressControllerIntegrationTest(CustomWebApplicationFactory<Startup> factory)
             {
                 _client = factory.CreateClient(new WebApplicationFactoryClientOptions
                 {
                     AllowAutoRedirect = false
                 });
             }
    
             private readonly HttpClient _client;
    
             [Fact]
             public async Task Get_AllAddressAndRetrieveAddress()
             {
                 const string allAddressUri = "/api/Address/GetAll";
                 HttpResponseMessage allAddressesHttpResponse = await _client.GetAsync(allAddressUri);
    
                 allAddressesHttpResponse.EnsureSuccessStatusCode();
    
                 string allAddressStringResponse = await allAddressesHttpResponse.Content.ReadAsStringAsync();
                 var addresses = JsonConvert.DeserializeObject<IList<AddressDto.AddressDto>>(allAddressStringResponse);
                 Assert.Equal(3, addresses.Count);
    
                 AddressDto.AddressDto address = addresses.First();
                 string retrieveUri = $"/api/Address/Retrieve?id={address.ID}";
                 HttpResponseMessage addressHttpResponse = await _client.GetAsync(retrieveUri);
    
                 // Must be successful.
                 addressHttpResponse.EnsureSuccessStatusCode();
    
                 // Deserialize and examine results.
                 string addressStringResponse = await addressHttpResponse.Content.ReadAsStringAsync();
                 var addressResult = JsonConvert.DeserializeObject<AddressDto.AddressDto>(addressStringResponse);
                 Assert.Equal(address.ID, addressResult.ID);
                 Assert.Equal(address.Province, addressResult.Province);
                 Assert.Equal(address.City, addressResult.City);
                 Assert.Equal(address.County, addressResult.County);
             }
         }
     }
  5. 在測試資源管理器中運行集成測試方法。

  6. 結果。

  7. 至此,集成測試完成。需要注意的是,集成測試往往耗時比較多,所以建議能使用單元測試時就不要使用集成測試。

總結:當我們寫單元測試時,一般不會同時存在 Stub 和 Mock 兩種模擬對象,當同時出現這兩種對象時,表明單元測試寫的不合理,或者業務寫的太過龐大,同時,我們可以通過單元測試驅動業務代碼重構。當需要重構時,我們應盡量完成重構,不要留下欠下過多技術債務。集成測試有自身的複雜度存在,我們不要節約時間而打破單一職責原則,否則會引發不可預期後果。為了應對業務修改,我們應該在業務修改以後,進行回歸測試,回歸測試主要關注被修改的業務部分,同時測試用例如果有沒要可以重寫,運行整個和修改業務有關的測試用例集。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

分類
發燒車訊

variable precision SWAR算法

      計算二進制形式中1的數量這種問題,在各種刷題網站上比較常見,以往都是選擇最笨的遍歷方法“矇混”過關。在了解Redis的過程中接觸到了variable precision SWAR算法(以下簡稱VP-SWAR算法),算法異常簡潔,是目前已知的同類方法中最快的。但如果對於位運算不是很熟悉的話,卻不一定容易理解,所以有必要記錄一下。

      下面先看看VP-SWAR算法的完整實現,然後再逐行解釋。

  public int vpSWAR(int i){
    i = (i & 0x55555555) + ((i>>1) & 0x55555555);
    i = (i & 0x33333333) + ((i>>2) & 0x33333333);
    i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
    i = (i * 0x01010101) >> 24;
    return i;
  }

      VP-SWAR算法分為四步,第一步

i = (i & 0x55555555) + ((i>>1) & 0x55555555);

      第一步的作用是計算每兩位為一組的二進制形式包含1的個數。要理解這句話,我們需要從二進制的角度看看到底發生了什麼。首先, 0x55555555 的二進製表示為 0101 0101 0101 0101 0101 0101 0101 0101 ,這個数字的規律是基數位為1,偶數位為0。為簡單起見,我們只考慮兩位,總共有四種情況,即:

 

i b i & b 結果
00 01 00
01 01 01
10 01 00
11 01 01

       觀察發現, i & (0b01) 是i的基數位對應b的1位,i的偶數位對應着b的0位, i & (0b01) 的結果會將I的偶數位置為0,而基數位保持不變,得到的結果就是i的基數位包含1的個數。 (i >> 1) & 0x55555555 先將i右移一位,也就是將i的基數位對應b的0位,i的偶數位對應着b的1位,然後再與 0x55555555 按位與,計算出來的是i的偶數位包含1的個數。兩個計算結果相加就得到i每兩位為一組中包含的1的數量,我們最後需要的就是這每兩位一組的和。

      第二步是在第一步的基礎上,計算每四位為一組包含1的個數。按照每2位為一組分組用到了 0x55555555 這個數,那麼自然的,按照每4位為一組分組自然就需要 0b0011 這種形式,這就是使用 0x33333333 的原因。理論上, i & (0b0011) 總共有16種情況,但是四位二進制位最多包含4個1,用二進製表示為 0b0100 ,所以經過第一步之後,i最多有5種取值,如下:

i b i & b 結果
0000 0011 0000
0001 0011 0001
0010 0011 0010
0011 0011 0011
0100 0011 0000

 

      觀察發現, i & (0b0011) 得到的是i的低兩位包含的1的個數,  (i >> 2) & 0b0011 )得到的是i的高兩位包含的1的個數,兩個結果相加得到每四位包含的1的個數。注意,這裏並不是說任何數與 0b0011 按位與得到的都是低兩位包含的1的個數,這裏的前提是第一步的計算,因為經過第一步計算之後,每兩位包含多少個1已經記錄了下來,再和 0b0011 按位與才得到正確的結果。例如, 0x0010 & 0x 0011=0x0010 ,但是我們不能說 0x0010 包含兩個1,但是如果 0x0010 是經過第一步的計算得來,那才說明 0x0010 記錄原始數據低兩位有兩個1。

      第三步在第二步基礎上,計算每8位有多少個1,由 0x010x0011 ,很自然想到 0x00001111 ,其對應的32位的十六進制數就是 0x0F0F0F0F

      第四步就很有意思了,它不再是計算每16位包含1的個數,而是直接計算32位包含1的個數。對於32位的數來說,可以將其按每8位一組分為4組,分別用ABCD表示,例如 0x01020304 用這種形式表示為:

 

 

      假設 0x01020304 是經過前三步計算之後得到的結果,那麼要計算其總共包含多少個1,只需計算A+B+C+D。而ABCD表示的是不同的位區間範圍,不能直接相加,該如何快速計算A+B+C+D的值呢?這裏又用到了移位運算,將B、C、D分別左移8位、16位、24位,使其分別與A對齊:

 

       我們發現,將数字i分別左移0位、8位、16位、24位然後相加的結果,就是 i * 0x01010101 ,因為 i + (i << 8) + (i << 16) + (i << 24) = i * (1 + 1 << 8 + 1 << 16 + 1 << 24) = i * 0x01010101 。對於32位数字來說,左移之後超過32位的部分會被捨棄,低位補0,將左移之後得到的四個数字相加,結果的高8位的值就是原32位數包含的1的個數,要得到這個值,只需要將結果右移24位,將值放在低8位即可。

      到這裏,整個算法就結束了,右移的結果就是1的數量。在Redis中,BITCOUNT命令同時使用了查表法和VP-SWAR這兩種方法。當要計算的位數小於128位時,使用查表法,否則使用VP-SWAR算法。其中查表法的做法是,程序先存一個256長度的表,按順序記錄從0-255(即 0b00000000 – 0b11111111) 數中二進制1的個數,然後對於輸入參數每8位查一次表。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!