# 點網的異步程式模型
## 前言
從現在回頭看,還存在 .NET 裡面的非同步模型有三種:
* Event-based Asynchronous Model(EAM): methods + event handlers
* Asynchronous Programming Model(APM): Begin + End methods + IAsyncResult
* Task-based Asynchronous Programming(TAP) Model:Task object + (async/await)
在更早之前的就先跳過不討論。從這三種來看看差異在哪裡。
## Event-based Asynchronous Model(EAM)
第一種在 System.Net.WebClient 還看得到,例如,它使用 DownloadDataAsync 與 DownloadDataCompleted 來完成非同步操作。
```
public partial class Form1 : Form
{
System.Net.WebClient webClient;
public Form1()
{
InitializeComponent();
webClient = new System.Net.WebClient();
webClient.DownloadDataCompleted += WebClient_DownloadDataCompleted;
webClient.DownloadDataAsync(new Uri("http://www.google.com"), "[form constructor trigger]");
Debug.WriteLine("[form constructor]");
}
private void WebClient_DownloadDataCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
Debug.WriteLine((string)e.UserState);
Debug.WriteLine(BitConverter.ToString(e.Result));
}
}
```
首先要先註冊 DownloadDataCompleted 發生時,要用自己寫的哪個函式來處理,範例中是用WebClient_DownloadDataCompleted 來接。當我們呼叫完 DownloadDataAsync 之後,就是等待事情做完,然後 WebClient_DownloadDataCompleted 會被某個 thread 呼叫執行,我們要做的事情就是在這裡寫好。如果有需要分辨不同需求的 DownloadDataAsync 所產生的 WebClient_DownloadDataCompleted,則可以把資訊放在 DownloadDataAsync 的第二個參數,由 e.UserState 拿回攜帶的資訊。
"[form constructor]" 在網頁內容之前就先寫出來,代表不是 blocking。
## Asynchronous Programming Model(APM)
第二種,則在新一點的物件中出現,像是 System.Net.WebRequest。例如,它使用 BeginGetResponse 與 AsyncCallback 這種 delegate callback 合體使用。
```
public Form1()
{
InitializeComponent();
System.Net.WebRequest webRequest = System.Net.WebRequest.Create("http://www.google.com");
webRequest.BeginGetResponse(new AsyncCallback(
(IAsyncResult ar) =>
{
System.Net.WebRequest req = (System.Net.WebRequest)ar.AsyncState;
System.Net.WebResponse resp = req.EndGetResponse(ar);
long cl = resp.ContentLength;
byte[] buffer = new byte[cl];
resp.GetResponseStream().Read(buffer, 0, (int)cl);
Debug.WriteLine(BitConverter.ToString(buffer));
}
), webRequest);
Debug.WriteLine("[form constructor]");
}
```
使用上,在 BeginGetResponse 把 callback 一同放進呼叫參數裡,我在這裡故意使用箭頭函式寫法,這樣可以顯出與 EAM 的不同。關注點就不同跳開在程式碼太遠的地方。當然也可以先準備好一個函式,呼叫 BeginGetResponse 時放進去。這樣的程式長像就會類似 EAM。像這樣,BeginGetResponse 與 callback 的對應就不容易錯亂。callback 攜帶資訊的地方在 BeginGetResponse 的第二個參數,callback 裡則用 ar.AsyncState 拿回來。
## Task-based Asynchronous Programming(TAP) Model
第三種又在新一點,它在 System.Net.Http.HttpClient 有用到。例如它使用 GetAsync 拿到 Task 物件,再利用 task 的操作拿到想要的內容。
```
public Form1()
{
InitializeComponent();
System.Net.Http.HttpClient httpClient;
httpClient = new System.Net.Http.HttpClient();
Task<System.Net.Http.HttpResponseMessage> taskHttpResponseMessage = httpClient.GetAsync("http://www.google.com");
taskHttpResponseMessage.ContinueWith(new Action<Task<System.Net.Http.HttpResponseMessage>>(
(Task<System.Net.Http.HttpResponseMessage> t) =>
{
System.Net.Http.HttpResponseMessage httpResponseMessage = taskHttpResponseMessage.Result;
byte[] c = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
Debug.WriteLine(BitConverter.ToString(c));
}
));
Debug.WriteLine("[form constructor]");
}
```
在這裡,GetAsync 拿到 Task 物件,然後在 ContinueWith 裡使用箭頭函式處理呼叫成功之後該做什麼事。這樣子寫,程式看起來還像是 APM 的樣子,但是 Task 物件有自己攜帶 wait 之類的方法,在控制要不要 blocking 的時候更自由。
## Task-based Asynchronous Programming(TAP) Model + async/await
TAP 模式寫起來還是很像 APM 模式,多層疊或是流程長的話,程式碼也還是不容易看。但是在 async/await 修飾字出現之後,程式碼就好看很多。其原理僅是靠編譯器幫忙。由於 async/await 無法在 constructor 使用,我程式移到一個 getPage() 驅動。上面的例子就變得跟同步程式長得很像。
```
public Form1()
{
InitializeComponent();
getPage();
Debug.WriteLine("[form constructor]");
}
async void getPage()
{
System.Net.Http.HttpClient httpClient;
httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("http://www.google.com");
byte[] c = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
Debug.WriteLine(BitConverter.ToString(c));
}
```
## 總結
有時候會看到上面三述方法混和提供的物件,以後就可以按照需要自由取用。就功能來說這三種做法可以互相取代。當然以團隊來說,可以自己決定的話,就統一一種會比較好。
## 參考
* https://docs.microsoft.com/zh-tw/dotnet/standard/asynchronous-programming-patterns/index
* https://www.infoq.com/articles/Tasks-Async-Await
2019年1月16日 星期三
2019年1月6日 星期日
[git] 不想一直輸入帳號密碼
如果每次跟遠端更新都要輸入密碼,有好處就是讓自己不會忘記密碼,但是有時要全部專案同時更新備份下來,寫了個批次檔來做這事,卻會停下來一個個輸入密碼就很討厭。這時候就加上一個設定,就可以讓輸入過的密碼自己記下來省掉麻煩。
在 windows 這裡要這樣設定:
在 windows 這裡要這樣設定:
git config --global credential.helper wincred關於 timeout 的設定就沒有看到了。
2019年1月3日 星期四
[點網] Asynchronous Programming Model(APM) 改成 blocking 模式
需求就是要把 BeginInvoke + EndInvoke + IAsyncResult 這種非同步模式改成同步模式。
似乎應該說是阻塞(Blocking)模式。
需要的是用到 System.Threading.AutoResetEvent 這個物件。
注意 signal.Set() 與 signal.WaitOne() 的位置
還有 signal 宣告的範圍
byte[] ReadTheFile(string filepath)
{
var signal = new AutoResetEvent(false);
var buf = new byte[1024];
var stream = File.OpenRead(filepath);
int readCount = 0;
stream.BeginRead(buf, 0, buf.Length, new AsyncCallback(asyncResult =>
{
FileStream myStream = (FileStream)asyncResult.AsyncState;
readCount = myStream.EndRead(asyncResult);
signal.Set();
}), stream);
signal.WaitOne();
stream.Dispose();
var result = new byte[readCount];
buf.CopyTo(result, 0);
return result;
}
我這麼做的原因是想把只有 APM 的改成 TAP,先把 APM 改成 block 再改成 TAP。也許將來有更好的方法。
似乎應該說是阻塞(Blocking)模式。
需要的是用到 System.Threading.AutoResetEvent 這個物件。
注意 signal.Set() 與 signal.WaitOne() 的位置
還有 signal 宣告的範圍
byte[] ReadTheFile(string filepath)
{
var signal = new AutoResetEvent(false);
var buf = new byte[1024];
var stream = File.OpenRead(filepath);
int readCount = 0;
stream.BeginRead(buf, 0, buf.Length, new AsyncCallback(asyncResult =>
{
FileStream myStream = (FileStream)asyncResult.AsyncState;
readCount = myStream.EndRead(asyncResult);
signal.Set();
}), stream);
signal.WaitOne();
stream.Dispose();
var result = new byte[readCount];
buf.CopyTo(result, 0);
return result;
}
我這麼做的原因是想把只有 APM 的改成 TAP,先把 APM 改成 block 再改成 TAP。也許將來有更好的方法。
2018年12月25日 星期二
[點網] .NET 的擴充方法
.NET 的 擴充方法 (C#/VB.NET)
https://docs.microsoft.com/zh-tw/dotnet/visual-basic/programming-guide/language-features/procedures/extension-methods
https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
不用繼承,也不用改變原始程式的情況下,替類別新增新的方法。
## VB .NET
要 Imports System.Runtime.CompilerServices 。
要在 Module 宣告。若要給 dll 外部使用,Module 要宣告 Public。
要擴充的方法上面要加上 <Extension()>,該方法或函式的第一個參數就是要擴充的型別。
可擴充的型別有:
類別 (參考類型)
結構 (實值類型)
介面
委派
ByRef 和 ByVal 引數
泛型方法的參數
陣列
## C#
要定義在 static 物件中的 static 方法。
方法的第一個參數要加上 this 型別。
擴充方法是定義成靜態方法,但透過執行個體方法語法呼叫。
https://docs.microsoft.com/zh-tw/dotnet/visual-basic/programming-guide/language-features/procedures/extension-methods
https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
不用繼承,也不用改變原始程式的情況下,替類別新增新的方法。
## VB .NET
要 Imports System.Runtime.CompilerServices 。
要在 Module 宣告。若要給 dll 外部使用,Module 要宣告 Public。
要擴充的方法上面要加上 <Extension()>,該方法或函式的第一個參數就是要擴充的型別。
可擴充的型別有:
類別 (參考類型)
結構 (實值類型)
介面
委派
ByRef 和 ByVal 引數
泛型方法的參數
陣列
## C#
要定義在 static 物件中的 static 方法。
方法的第一個參數要加上 this 型別。
擴充方法是定義成靜態方法,但透過執行個體方法語法呼叫。
2018年12月24日 星期一
[心得]幾種 系統內溝通 的方式
幾種 系統內溝通 的方式
用 ros 的名詞:topic, service, actionlib
1. topic 方式,有兩個習慣模式
eventbus: event listen/event raise
topic: publish/subscribe
兩者極為相似,也常常被當成一樣的東西。依我的定義,其中不同的地方是:
topic 在系統中,幾乎只有單程,沒有去與回的對應。接收方不需要與發送方互動時使用。也可不理會重覆傳送的問題。
eventbus 在系統中,有可能會存在著事件去與回的處理,每次事件會在乎重覆傳送的判定問題。
但因為這兩種也都可以用簡單程式方式解決掉彼此的差異,所以視為一樣也沒有什麼問題。
2. service 方式
也就是 http 採用的模式,也就是 request/response。必定由 client 發起 request,由 server 送回 response。
3. actionlib 方式
由 client 設定 goal,server 定期或定時回報 progress ,當最後結束的時候回傳 result 的模式。在進行的過程中,還可以 cancel 中斷執行以及查看 status。
這種模式也可由前兩種組合, request/response + event 來做到。流程稍有不同。
client 送出 request,server 回應 response 是否執行,然後由 server 發送 event 來通知 client 進度與結果。
用 ros 的名詞:topic, service, actionlib
1. topic 方式,有兩個習慣模式
eventbus: event listen/event raise
topic: publish/subscribe
兩者極為相似,也常常被當成一樣的東西。依我的定義,其中不同的地方是:
topic 在系統中,幾乎只有單程,沒有去與回的對應。接收方不需要與發送方互動時使用。也可不理會重覆傳送的問題。
eventbus 在系統中,有可能會存在著事件去與回的處理,每次事件會在乎重覆傳送的判定問題。
但因為這兩種也都可以用簡單程式方式解決掉彼此的差異,所以視為一樣也沒有什麼問題。
2. service 方式
也就是 http 採用的模式,也就是 request/response。必定由 client 發起 request,由 server 送回 response。
3. actionlib 方式
由 client 設定 goal,server 定期或定時回報 progress ,當最後結束的時候回傳 result 的模式。在進行的過程中,還可以 cancel 中斷執行以及查看 status。
這種模式也可由前兩種組合, request/response + event 來做到。流程稍有不同。
client 送出 request,server 回應 response 是否執行,然後由 server 發送 event 來通知 client 進度與結果。
2018年10月5日 星期五
[點網核] win2003, win7 與 .net core 不是很熟的樣子
.net core 搞半天,win2003 不認得它……
win7 也跟它不熟……
如果是抱怨 hostfxr.dll 無法載入,那就安裝以下更新
https://www.microsoft.com/en-us/download/details.aspx?id=26764
KB2533623
win7 也跟它不熟……
如果是抱怨 hostfxr.dll 無法載入,那就安裝以下更新
https://www.microsoft.com/en-us/download/details.aspx?id=26764
KB2533623
2018年9月25日 星期二
[點網][超速譯]VS2017 的中斷點
https://blogs.msdn.microsoft.com/visualstudio/2018/09/13/how-can-i-pause-my-code-in-visual-studio-breakpoints-faq/
Visual Studio 是我用過最好用的 IDE,沒有之一。
中斷點這件事也是我看過不少碼農不甚使用的事。正好有官方提出一些小技巧給大家知道。
快速摘譯重點,成為超速譯的一篇。
設立 breakpoint
(1)左點左邊界 或是 按F9
(2)按F5
管理 breakpoint
(1)breakpoint window
(2) Debug -> Window -> Breakpoints
Conditional Breakpoint
(1)設定 breakpoint
(2)鼠標飄到 breakpoint 上,按下齒輪圖示
(3)選擇 Conditions,然後設定條件
(4)條件輸入完畢,關閉設定窗
Iteration breakpoint
(1)設定 breakpoint
(2)鼠標飄到 breakpoint 上,按下齒輪圖示
(3)選擇 Conditions,然後設定條件為 Hit Count
Function breakpoint
(1) Debug -> New Breakpoint -> Break at Function
Value change breakpoint
* C++, data breakpoints
* Watch Window or the Breakpoints Window 右點 變數 選擇 Break when value changes
* managed code, 針對某個 instance 的屬性偵測改變
(1)在 break mode,右點物件選擇 Make Object ID
(2)在欲偵測的屬性 setter 加入一個 conditional breakpoint 條件是 this == $1
(3)按F5,會停在 setter
(4)在 Call Stack 雙點前一個 frame 可以看到改變屬性的 code 是哪一行
exception breakpoint
在 Exception Settings 窗,設定哪些 exception 要停下來
call stack breakpoint
(1)Debug -> Windows -> Call Statck
(2)右點 calling function,選擇 Breakpoint -> Insert Breakpoint
disassembly breakpoint
(1)打開 disassembly window, Debug -> Windows -> Disassembly
(2)左點左邊界或按F9
Visual Studio 是我用過最好用的 IDE,沒有之一。
中斷點這件事也是我看過不少碼農不甚使用的事。正好有官方提出一些小技巧給大家知道。
快速摘譯重點,成為超速譯的一篇。
設立 breakpoint
(1)左點左邊界 或是 按F9
(2)按F5
管理 breakpoint
(1)breakpoint window
(2) Debug -> Window -> Breakpoints
Conditional Breakpoint
(1)設定 breakpoint
(2)鼠標飄到 breakpoint 上,按下齒輪圖示
(3)選擇 Conditions,然後設定條件
(4)條件輸入完畢,關閉設定窗
Iteration breakpoint
(1)設定 breakpoint
(2)鼠標飄到 breakpoint 上,按下齒輪圖示
(3)選擇 Conditions,然後設定條件為 Hit Count
Function breakpoint
(1) Debug -> New Breakpoint -> Break at Function
Value change breakpoint
* C++, data breakpoints
* Watch Window or the Breakpoints Window 右點 變數 選擇 Break when value changes
* managed code, 針對某個 instance 的屬性偵測改變
(1)在 break mode,右點物件選擇 Make Object ID
(2)在欲偵測的屬性 setter 加入一個 conditional breakpoint 條件是 this == $1
(3)按F5,會停在 setter
(4)在 Call Stack 雙點前一個 frame 可以看到改變屬性的 code 是哪一行
exception breakpoint
在 Exception Settings 窗,設定哪些 exception 要停下來
call stack breakpoint
(1)Debug -> Windows -> Call Statck
(2)右點 calling function,選擇 Breakpoint -> Insert Breakpoint
disassembly breakpoint
(1)打開 disassembly window, Debug -> Windows -> Disassembly
(2)左點左邊界或按F9
訂閱:
文章 (Atom)