今天老師上課講到了異步編程。
一個異步過程調用發出後,調用者不能立刻得到結果。基於事件機制,實際處理這個調用的部件在完成後,通過狀態、通知和回調來通知調用者。比如,你現在要有一批數據要大數據要入庫,你又不想一邊入庫一邊等待返回結果,你可以用異步,將大數據推入一個隊列,然後另外一個線程來操作這個隊列裏面的數據入庫,入完了,就通知一下主線程。這段時間你的主線程可以做任何事。
首先提到了Asynchromous Programming Model,異步編寫模型是一種模式,該模式允許用更少的線程去做更多的操作,.NET
Framework很多類也實現了該模式,同時我們也可以自定義類來實現該模式,(也就是在自定義的類中實現返回類型爲IAsyncResult接口的BeginXXX方法和EndXXX
方法)委託類型也定義了BeginInvoke和EndInvoke方法。
以FileStream 爲例
同步方法
public override int Read(byte[] array,int offset, int count);
異步方法
public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject);
結束異步的操作:
public override int EndRead(IAsyncResult asyncResult);
APM提供了四種方式供開發人員選擇:
1. 在調用BeginXxx方法的線程上調用EndXxx方法來得到異步操作的結果,但是這種方式會阻塞調用線程,知道操作完成之後調用線程才繼續運行
2. 查詢IAsyncResult的syncWaitHandle屬性,從而得到WaitHandle,然後再調用它的WaitOne方法來使一個線程阻塞並等待操作完成再調用EndXxx方法來獲得操作的結果。
3. 循環查詢IAsyncResult的IsComplete屬性,操作完成後再調用EndXxx方法來獲得操作返回的結果。
4. 使用 AsyncCallback委託來指定操作完成時要調用的方法,在操作完成後調用的方法中調用EndXxx操作來獲得異步操作的結果。
推薦使用第四種方法:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Specialized;
using System.Collections;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class UseDelegateForAsyncCallback
{
static int requestCounter;
static ArrayList hostData = new ArrayList();
static StringCollection hostNames = new StringCollection();
static void UpdateUserInterface()
{
// Print a message to indicate that the application
// is still working on the remaining requests.
Console.WriteLine("{0} requests remaining.", requestCounter);
}
public static void Main()
{
// Create the delegate that will process the results of the
// asynchronous request.
AsyncCallback callBack = new AsyncCallback(ProcessDnsInformation);
string host;
do
{
Console.Write(" Enter the name of a host computer or <enter> to finish: ");
host = Console.ReadLine();
if (host.Length > 0)
{
// Increment the request counter in a thread safe manner.
Interlocked.Increment(ref requestCounter);
// Start the asynchronous request for DNS information.
Dns.BeginGetHostEntry(host, callBack, host);
}
} while (host.Length > 0);
// The user has entered all of the host names for lookup.
// Now wait until the threads complete.
while (requestCounter > 0)
{
UpdateUserInterface();
}
// Display the results.
for (int i = 0; i< hostNames.Count; i++)
{
object data = hostData [i];
string message = data as string;
// A SocketException was thrown.
if (message != null)
{
Console.WriteLine("Request for {0} returned message: {1}",
hostNames[i], message);
continue;
}
// Get the results.
IPHostEntry h = (IPHostEntry) data;
string[] aliases = h.Aliases;
IPAddress[] addresses = h.AddressList;
if (aliases.Length > 0)
{
Console.WriteLine("Aliases for {0}", hostNames[i]);
for (int j = 0; j < aliases.Length; j++)
{
Console.WriteLine("{0}", aliases[j]);
}
}
if (addresses.Length > 0)
{
Console.WriteLine("Addresses for {0}", hostNames[i]);
for (int k = 0; k < addresses.Length; k++)
{
Console.WriteLine("{0}",addresses[k].ToString());
}
}
}
}
// The following method is called when each asynchronous operation completes.
static void ProcessDnsInformation(IAsyncResult result)
{
string hostName = (string) result.AsyncState;
hostNames.Add(hostName);
try
{
// Get the results.
IPHostEntry host = Dns.EndGetHostEntry(result);
hostData.Add(host);
}
// Store the exception message.
catch (SocketException e)
{
hostData.Add(e.Message);
}
finally
{
// Decrement the request counter in a thread-safe manner.
Interlocked.Decrement(ref requestCounter);
}
}
}
}
C# 5.0 async / await異步對可能起阻止作用的活動(例如,應用程序訪問 Web 時)至關重要。 對Web 資源的訪問有時很慢或會延遲。 如果此類活動在同步過程中受阻,則整個應用程序必須等待。 在異步過程中,應用程序可繼續執行不依賴 Web 資源的其他工作,直至潛在阻止任務完成。
async Task<int> AccessTheWebAsync()
{
<span style="white-space:pre"> </span>HVpClient client = new HVpClient();
<span style="white-space:pre"> </span>Task<string> getStringTask = client.GetStringAsync("hVp://msdn.microsod.com");
<span style="white-space:pre"> </span>DoIndependentWork();
<span style="white-space:pre"> </span>string urlContents = await getStringTask;
<span style="white-space:pre"> </span>return urlContents.Length;
}
HttpClient是接收HttpResponseMessages和發送HttpRequestMessages的主要類,如果你習慣了使用WebClient或者是HttpWebRequest, 需要注意HttpClient和他們
不同的地方:
1、在HttpClient實例上配置擴展,設置默認的頭部,取消未完成的請求和更多的設置。
2、你通過一個單一的HttpClient實例,它有自己的連接池。
3、HttpClients不與特定的HTTP服務器綁定,你可以使用相同的HttpClient實例提交任何HTTP請求。
4、你可以用HttpClient爲特定的站點創建特殊的Client
5、HttpClient採用新的型模式處理異步請求使它更容易管理和協調更多的請求。
HttpClient - Get
<pre name="code" class="csharp">client.GetAsync(_address).ConenueWith((requestTask)=>
{
//Get HTTP response from completed task.
HttpResponseMessage response = requestTask.Result;
//Check that response was successful or throw exception
response.EnsureSuccessStatusCode();
//Read response asynchronously as JsonValue and write out top facts for each country
response.Content.ReadAsAsync<JsonArray>().ConenueWith((readTask)=>{
Console.WriteLine("First 50 countries listed by The World Bank...");
foreach(var country in readTask.Result[1])
{
Console.WriteLine("{0},Country Code:{1},Capital:{2},Laetude:{3},Longitude:{4}",country.Value["name"],
country.Value["iso2Code"],country.Value["capitalCity"],country.Value["laetude"],country.Value["longitude"]);
}
});
});
HttpClient-PostUri serviceReq = new Uri(serviceAddress);
HttpClient client = new HttpClient();
HttpContent content = new StringContent(@"{ ""value"": ""test""}");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// Send a request asynchronously continue when complete
client.PostAsync(serviceReq, content).ContinueWith(
(requestTask) =>
{
// Get HTTP response from completed task.
HttpResponseMessage response = requestTask.Result;
// Check that response was successful or throw exception
response.EnsureSuccessStatusCode();
// Read response asynchronously as JsonValue and write out top facts for each country
response.Content.ReadAsAsync<string>().ContinueWith(
(readTask) =>
{
Console.WriteLine(readTask.Result);
});
});