[轉] C#異步操作

Title

通過委托實現異步調用中BeginInvoke及回調函數的使用

通過委托實現異步調用的步驟:

1.定義委托。

2.將要進行異步調用的方法“實例化”到定義的委托。

3.在委托上調用BeginInvoke方法。其中,BeginInvoke的參數由三個部分構成。第一部分:所定義的委托的函數簽名。

第二部分:希望調用的回調函數的委托。第三部分:自定義委托的實例(該實例將會在回調函數中的IAsyncResult的AsyncRState屬性中重構出我們在步驟2中定義的委托實例,并借助這個實例來調用EndInvoke方法。)

4.如果我們希望在當前線程來處理異步調用的結果,則可以使用BeginInvoke方法返回一個IAsyncResult實例(例如ar)

并在當前線程等待。如果我們希望在異步線程中通過回調函數來處理結果,則我們需要在3中傳遞一個回調委托,并在該處理中調用EndInvoke方法。

以下是一段Programming C#(4版)中的一段實例:

ContractedBlock.gifExpandedBlockStart.gifCode
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Threading;

namespace?property
{
????
public?class?DelegateClass
????{
????????
public?delegate?int?AsyncSampDelegate();
????????
public?event?AsyncSampDelegate?delEvent;

????????
public?void?Run()
????????{
????????????Console.WriteLine(
"The?Run?Thread?is?{0}",?Thread.CurrentThread.GetHashCode());
????????????
foreach?(AsyncSampDelegate?del?in?delEvent.GetInvocationList())
????????????{
????????????????del.BeginInvoke(
new?AsyncCallback(ReturnAsync),?del);
????????????}
????????}

????????
public?void?ReturnAsync(IAsyncResult?ar)
????????{
????????????
//獲得調用委托實例的引用
????????????AsyncSampDelegate?del?=?(AsyncSampDelegate)ar.AsyncState;
????????????
int?result?=?del.EndInvoke(ar);
????????????Console.WriteLine(
"The?result?is?{0},The?Thread?is?{1}",?result,?Thread.CurrentThread.GetHashCode());
????????}
????}

????
public?class?FirstSubscribe
????{
????????
private?int?myCount?=?0;

????????
public?void?AddFunToDel(DelegateClass?tmpDel)
????????{
????????????tmpDel.delEvent
+=new?DelegateClass.AsyncSampDelegate(FirstFun);
????????}


????????
public?int?FirstFun()
????????{
????????????
return?myCount++;
????????}
????}

????
public?class?SecondSubscribe
????{
????????
private?int?myCount?=?0;

????????
public?void?AddFunToDel(DelegateClass?tmpDel)
????????{
????????????tmpDel.delEvent
+=new?DelegateClass.AsyncSampDelegate(SecondFun);
????????}

????????
public?int?SecondFun()
????????{
????????????
return?myCount?+=?2;
????????}
????}

????
public?class?App
????{
????????
static?void?Main()
????????{
????????????DelegateClass?delClass?
=?new?DelegateClass();
????????????FirstSubscribe?fs?
=?new?FirstSubscribe();
????????????SecondSubscribe?ss?
=?new?SecondSubscribe();

????????????fs.AddFunToDel(delClass);
????????????ss.AddFunToDel(delClass);

????????????Console.WriteLine(
"The?Main?Thread?is?{0}",?Thread.CurrentThread.GetHashCode());
????????????delClass.Run();
????????????Console.Read();
????????}
????}
}?

?

?

?

多線程和異步操作的異同

  多線程和異步操作兩者都可以達到避免調用線程阻塞的目的,從而提高軟件的可響應性。甚至有些時候我們就認為多線程和異步操作是等同的概念。但是,多線程和異步操作還是有一些區別的。而這些區別造成了使用多線程和異步操作的時機的區別。

  異步操作的本質

   所有的程序最終都會由計算機硬件來執行,所以為了更好的理解異步操作的本質,我們有必要了解一下它的硬件基礎。 熟悉電腦硬件的朋友肯定對DMA這個詞不陌生,硬盤、光驅的技術規格中都有明確DMA的模式指標,其實網卡、聲卡、顯卡也是有DMA功能的。DMA就是直 接內存訪問的意思,也就是說,擁有DMA功能的硬件在和內存進行數據交換的時候可以不消耗CPU資源。只要CPU在發起數據傳輸時發送一個指令,硬件就開 始自己和內存交換數據,在傳輸完成之后硬件會觸發一個中斷來通知操作完成。這些無須消耗CPU時間的I/O操作正是異步操作的硬件基礎。所以即使在DOS 這樣的單進程(而且無線程概念)系統中也同樣可以發起異步的DMA操作。

  線程的本質

  線程不是一個計算機硬件的功能,而是操作系統提供的一種邏輯功能,線程本質上是進程中一段并發運行的代碼,所以線程需要操作系統投入CPU資源來運行和調度。

  異步操作的優缺點

   因為異步操作無須額外的線程負擔,并且使用回調的方式進行處理,在設計良好的情況下,處理函數可以不必使用共享變量(即使無法完全不用,最起碼可以減少 共享變量的數量),減少了死鎖的可能。當然異步操作也并非完美無暇。編寫異步操作的復雜程度較高,程序主要使用回調方式進行處理,與普通人的思維方式有些 初入,而且難以調試。

  多線程的優缺點

  多線程的優點很明顯,線程中的處理程序依然是順序執行,符合普通人的思維習慣,所以編程簡單。但是多線程的缺點也同樣明顯,線程的使用(濫用)會給系統帶來上下文切換的額外負擔。并且線程間的共享變量可能造成死鎖的出現。

  適用范圍

   在了解了線程與異步操作各自的優缺點之后,我們可以來探討一下線程和異步的合理用途。我認為:當需要執行I/O操作時,使用異步操作比使用線程+同步 I/O操作更合適。I/O操作不僅包括了直接的文件、網絡的讀寫,還包括數據庫操作、Web Service、HttpRequest以及.net Remoting等跨進程的調用。

  而線程的適用范圍則是那種需要長時間CPU運算的場合,例如耗時較長的圖形處理和算法執行。但是往 往由于使用線程編程的簡單和符合習慣,所以很多朋友往往會使用線程來執行耗時較長的I/O操作。這樣在只有少數幾個并發操作的時候還無傷大雅,如果需要處 理大量的并發操作時就不合適了。

  實例研究

  說了那么理論上的東西,可能有些兄弟早就不耐煩了,現在我們來研究幾個實際的異步操作例子吧。

  實例1:由delegate產生的異步方法到底是怎么回事?

  大家可能都知道,使用delegate可以“自動”使一個方法可以進行異步的調用。從直覺上來說,我覺得是由編譯器或者CLR使用了另外的線程來執行目標方法。到底是不是這樣呢?讓我們來用一段代碼證明一下吧。

?

ContractedBlock.gifExpandedBlockStart.gifCode
using?System;
using?System.Threading;

namespace?AsyncDelegateDemo
{
??
delegate?void?AsyncFoo(int?i);
??
class?Program
??{
????
///?<summary>
????
///?輸出當前線程的信息
????
///?</summary>
???
///?<param?name="name">方法名稱</param>

????
static?void?PrintCurrThreadInfo(string?name)
????{
??????Console.WriteLine(
"Thread?Id?of?"?+?name+?"?is:?"?+?Thread.CurrentThread.ManagedThreadId+?",?current?thread?is?"
??????
+?(Thread.CurrentThread.IsThreadPoolThread???""?:?"not?")
??????
+?"thread?pool?thread.");
????}

????
///?<summary>
????
///?測試方法,Sleep一定時間
????
///?</summary>
????
///?<param?name="i">Sleep的時間</param>
????static?void?Foo(int?i)
????{
???????PrintCurrThreadInfo(
"Foo()");
???????Thread.Sleep(i);
????}

????
///?<summary>
????
///?投遞一個異步調用
????
///?</summary>
????static?void?PostAsync()
????{
??????AsyncFoo?caller?
=?new?AsyncFoo(Foo);
??????caller.BeginInvoke(
1000,?new?AsyncCallback(FooCallBack),?caller);
????}

????
static?void?Main(string[]?args)
????{
??????PrintCurrThreadInfo(
"Main()");
??????
for(int?i?=?0;?i?<?10?;?i++)
??????{
?????????PostAsync();
??????}
??????Console.ReadLine();
????}

????
static?void?FooCallBack(IAsyncResult?ar)
????{
??????PrintCurrThreadInfo(
"FooCallBack()");
??????AsyncFoo?caller?
=?(AsyncFoo)?ar.AsyncState;
??????caller.EndInvoke(ar);
????}
??}
}?

這段代碼代碼的輸出如下:


Thread Id of Main() is: 1, current thread is not thread pool thread.

Thread Id of Foo() is: 3, current thread is thread pool thread.

Thread Id of FooCallBack() is: 3, current thread is thread pool thread.

Thread Id of Foo() is: 3, current thread is thread pool thread.

Thread Id of Foo() is: 4, current thread is thread pool thread.

Thread Id of Foo() is: 5, current thread is thread pool thread.

Thread Id of FooCallBack() is: 3, current thread is thread pool thread.

Thread Id of Foo() is: 3,

?

?///

http://www.cnsdn.com.cn/blog/article.asp?id=2164

///

?

  。NET?Framework?為異步操作提供了兩種設計模式:使用?IAsyncResult?對象的異步操作與使用事件的異步操作。先來學習前者

  概述

  IAsyncResult?異步設計模式通過名為?BeginOperationName?和?EndOperationName?的兩個方法來實現原同步方法的異步調用,如?FileStream?類提供了?BeginRead?和?EndRead?方法來從文件異步讀取字節,它們是?Read?方法的異步版本

  Begin?方法包含同步方法簽名中的任何參數,此外還包含另外兩個參數:一個AsyncCallback?委托和一個用戶定義的狀態對象。委托用來調用回調方法,狀態對象是用來向回調方法傳遞狀態信息。該方法返回一個實現?IAsyncResult?接口的對象

  End?方法用于結束異步操作并返回結果,因此包含同步方法簽名中的?ref?和?out?參數,返回值類型也與同步方法相同。該方法還包括一個?IAsyncResult?參數,用于獲取異步操作是否完成的信息,當然在使用時就必須傳入對應的?Begin?方法返回的對象實例

  開始異步操作后如果要阻止應用程序,可以直接調用?End?方法,這會阻止應用程序直到異步操作完成后再繼續執行。也可以使用?IAsyncResult?的?AsyncWaitHandle?屬性,調用其中的WaitOne等方法來阻塞線程。這兩種方法的區別不大,只是前者必須一直等待而后者可以設置等待超時

  如果不阻止應用程序,則可以通過輪循?IAsyncResult?的?IsCompleted?狀態來判斷操作是否完成,或使用?AsyncCallback?委托來結束異步操作。AsyncCallback?委托包含一個?IAsyncResult?的簽名,回調方法內部再調用?End?方法來獲取操作執行結果

  嘗試

  先來熟悉一下今天的主角,IAsyncResult?接口

public?interface?IAsyncResult
{
object?AsyncState?{?get;?}
WaitHandle?AsyncWaitHandle?{?get;?}
bool?CompletedSynchronously?{?get;?}
bool?IsCompleted?{?get;?}
}
????????????
?


  我用一個?AsyncDemo?類作為異步方法的提供者,后面的程序都會調用它。內部很簡單,構造函數接收一個字符串作為?name?,Run?方法輸出?"My?name?is?"?+?name?,而異步方法直接用委托的?BeginInvoke?和?EndInvoke?方法實現

public?class?AsyncDemo
{
//?Use?in?asynchronous?methods
private?delegate?string?runDelegate();
private?string?m_Name;
private?runDelegate?m_Delegate;
public?AsyncDemo(string?name)
{
m_Name?=?name;
m_Delegate?=?new?runDelegate(Run);
}
/**
///?Synchronous?method
///
///
public?string?Run()
{
return?"My?name?is?"?+?m_Name;
}
/**
///?Asynchronous?begin?method
///
///
///
///
public?IAsyncResult?BeginRun(AsyncCallback?callBack,?Object?stateObject)
{
try
{
return?m_Delegate.BeginInvoke(callBack,?stateObject);
}
catch(Exception?e)
{
//?Hide?inside?method?invoking?stack
throw?e;
}
}
/**
///?Asynchronous?end?method
///
///
///
public?string?EndRun(IAsyncResult?ar)
{
if?(ar?==?null)
throw?new?NullReferenceException("Arggument?ar?can't?be?null");
try
{
return?m_Delegate.EndInvoke(ar);
}
catch?(Exception?e)
{
//?Hide?inside?method?invoking?stack
throw?e;
}
}
????????????}?
?


  首先是?Begin?之后直接調用?End?方法,當然中間也可以做其他的操作

class?AsyncTest
{
static?void?Main(string[]?args)
{
AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(null,?null);
//?You?can?do?other?things?here
//?Use?end?method?to?block?thread?until?the?operation?is?complete
string?demoName?=?demo.EndRun(ar);
Console.WriteLine(demoName);
}
????????????}?
?


  也可以用?IAsyncResult?的?AsyncWaitHandle?屬性,我在這里設置為1秒超時

class?AsyncTest
{
static?void?Main(string[]?args)
{
AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(null,?null);
//?You?can?do?other?things?here
//?Use?AsyncWaitHandle.WaitOne?method?to?block?thread?for?1?second?at?most
ar.AsyncWaitHandle.WaitOne(1000,?false);
if?(ar.IsCompleted)
{
//?Still?need?use?end?method?to?get?result,?
//?but?this?time?it?will?return?immediately
string?demoName?=?demo.EndRun(ar);
Console.WriteLine(demoName);
}
else
{
Console.WriteLine("Sorry,?can't?get?demoName,?the?time?is?over");
}
}
????????????}?
?


  不中斷的輪循,每次循環輸出一個?"."

class?AsyncTest
{
static?void?Main(string[]?args)
{
AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(null,?null);
Console.Write("Waiting..");
while?(!ar.IsCompleted)
{
Console.Write(".");
//?You?can?do?other?things?here
}
Console.WriteLine();
//?Still?need?use?end?method?to?get?result,?
//?but?this?time?it?will?return?immediately
string?demoName?=?demo.EndRun(ar);
Console.WriteLine(demoName);
}
????????????}?
?


  最后是使用回調方法并加上狀態對象,狀態對象被作為?IAsyncResult?參數的?AsyncState?屬性被傳給回調方法。回調方法執行前不能讓主線程退出,我這里只是簡單的讓其休眠了1秒。另一個與之前不同的地方是?AsyncDemo?對象被定義成了類的靜態字段,以便回調方法使用

?class?AsyncTest
{
static?AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
static?void?Main(string[]?args)
{
//?State?object
bool?state?=?false;
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(new?AsyncCallback(outPut),?state);
//?You?can?do?other?thins?here
//?Wait?until?callback?finished
System.Threading.Thread.Sleep(1000);
}
//?Callback?method
static?void?outPut(IAsyncResult?ar)
{
bool?state?=?(bool)ar.AsyncState;
string?demoName?=?demo.EndRun(ar);
if?(state)
{
Console.WriteLine(demoName);
}
else
{
Console.WriteLine(demoName?+?",?isn't?it?");
}
}
????????????}?
?


  其他

  對于一個已經實現了?BeginOperationName?和?EndOperationName?方法的對象,我們可以直接用上述方式調用,但對于只有同步方法的對象,我們要對其進行異步調用也不需要增加對應的異步方法,而只需定義一個委托并使用其?BeginInvoke?和?EndInvoke?方法就可以了

?

轉載于:https://www.cnblogs.com/ruyi/archive/2009/07/14/1523510.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276453.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276453.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276453.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTTP Server Error 500 內部服務器錯誤

問題&#xff1a;HTTP500錯誤 或 Server Application Error ------------------------------------Server Application ErrorThe server has encountered an error while loading an application during the processing of your request. Please refer to the event log for mo…

使用 ohmyzsh 打造 windows、ubuntu、mac 系統高效終端命令行工具

如果覺得內容不錯&#xff0c;可以設為星標置頂我的公眾號原標題名&#xff1a;oh my zsh 和 windows git bash 設置別名提高效率寫于2018年06月03日在我的微信交流群中聽聞很多前端開發比較貧窮&#xff0c;沒有買mac電腦&#xff08;比如我&#xff09;&#xff0c;也沒有用過…

request獲取mac地址_【Go】獲取用戶真實的ip地址

原文鏈接&#xff1a;https://blog.thinkeridea.com/201903/go/get_client_ip.html用戶請求到達提供服務的服務器中間有很多的環節&#xff0c;導致服務獲取用戶真實的 ip 非常困難&#xff0c;大多數的框架及工具庫都會封裝各種獲取用戶真實 ip 的方法&#xff0c;在 exnet 包…

Installation of Apache HTTPD

轉載鏈接&#xff1a;http://www.linuxfromscratch.org/blfs/view/svn/server/apache.html Installation of Apache-2.4.7 HTTPD For security reasons, running the server as an unprivileged user and group is strongly encouraged. Create the following group and user…

iPhone開發四劍客之《Objective-C基礎教程》

iPhone 開發四劍客之《Objective-C 基礎教程》 Objective-C 語言是 C 語言的一個擴展集&#xff0c;許多&#xff08;可能是大多數&#xff09;具備 Mac OS X 外觀的應用程序都是使用該語言開發的。它以 C 語言為基礎&#xff0c;添加了一些微妙但意義重大的特性。 蘋果公司為…

教師節,你記憶中老師說過印象最深的是什么話?(抽獎)

我記憶中老師說過印象最深的話小學老師&#xff1a;1、小學語文老師李老師說&#xff0c;以后你們可能帶個手機就可以支付了~不需要帶現金。&#xff08;在杭州確實實現了&#xff0c;用支付寶即可&#xff09; 2、小學數學老師李老師說&#xff1a;好好讀書的目的是啥&#xf…

Spark List組件滾動條加事件使datalist數據發生變化

<?xml version"1.0" encoding"utf-8"?><!-- http://blog.flexexamples.com/2009/05/31/detecting-when-the-vertical-scroll-bar-is-scrolled-on-a-spark-list-control-in-flex-4/ --><s:Application name"Spark_List_scroller_vert…

keras訓練完以后怎么預測_還在使用“龜速”的單顯卡訓練模型?動動手,讓TPU節省你的時間...

點擊上方關注&#xff0c;All in AI中國本文將介紹如何使用Keras和Google CoLaboratory與TPU一起訓練LSTM模型&#xff0c;與本地計算機上的GPU相比&#xff0c;這樣訓練能大大縮短訓練時間。很長一段時間以來&#xff0c;我都在單張GTX 1070顯卡上訓練我的模型&#xff0c;它的…

PHP5加載|安裝外部C動態庫

[1] cd php-5.3.9/ext[2] ./ext_skel --extnamencdocxml[3] cd ncdocxml[4] nano -w config.m4############刪除 3 個 dnldnl PHP_ARG_WITH(my_module, for my_module support,dnl Make sure that the comment is aligned:dnl [ --with-my_module Include my_module support])或…

手把手教你寫個小程序定時器管理庫

背景凹凸曼是個小程序開發者&#xff0c;他要在小程序實現秒殺倒計時。于是他不假思索&#xff0c;寫了以下代碼&#xff1a;Page({init: function () {clearInterval(this.timer)this.timer setInterval(() > {// 倒計時計算邏輯console.log(setInterval)})}, })可是&…

[New Portal]Windows Azure Virtual Machine (14) 在本地制作數據文件VHD并上傳至Azure(1)

《Windows Azure Platform 系列文章目錄》 之前的內容里&#xff0c;我介紹了如何將本地的Server 2012中文版 VHD上傳至Windows Azure&#xff0c;并創建基于該Server 2012 VHD的虛擬機。 我們知道&#xff0c;VHD不僅僅可以保存操作系統&#xff0c;而且可以保存數據文件。 如…

python 退出程序_Python:用Ctrl+C解決終止多線程程序的問題!(建議收藏)

前言&#xff1a;今天為大家帶來的內容是Python:用CtrlC解決終止多線程程序的問題&#xff01;文章中的代碼具有不錯的參考意義&#xff0c;希望在此能夠幫助到各位&#xff01;(多數代碼用圖片的方式呈現出來&#xff0c;方便各位觀看與收藏)出發點&#xff1a;前段時間&#…

Mysql InnoDB Plugin安裝 install

轉載鏈接&#xff1a;http://www.orczhou.com/index.php/2010/03/innodb-plugin-setup/ InnoDB Plugin較之Built-in版本新增了很多特性&#xff1a;包括快速DDL、壓縮存儲等&#xff0c;而且引入了全新的文件格式Barracuda。眾多測試也表明&#xff0c;Plugin在很多方面優于Bu…

Hibernate的數據過濾查詢

數據過濾并不是一種常規的數據查詢方法&#xff0c;而是一種整體的篩選方法。數據過濾也可對數據進行篩選&#xff0c;因此&#xff0c;將其放在Hibernate的數據查詢框架中介紹。 如果一旦啟用了數據過濾器&#xff0c;則不管數據查詢&#xff0c;還是數據加載&#xff0c;該過…

若川知乎高贊:有哪些必看的 JS 庫?

歡迎星標我的公眾號&#xff0c;回復加群&#xff0c;長期交流學習我的知乎回答目前2w閱讀量&#xff0c;270贊&#xff0c;現在發到公眾號聲明原創。必看的js庫&#xff1f;只有當前階段值不值看。我從去年7月起看一些前端庫的源碼&#xff0c;歷時一年才寫了八篇《學習源碼整…

python用for循環求10的因數_python for循環練習(初級)

for循環練習1for i in range(4):print(i)D:\尚硅谷Python\venv\Scripts\python.exe D:/尚硅谷Python/test.py0123for循環練習2for x in range(1,40,5): #間隔5print(x)D:\尚硅谷Python\venv\Scripts\python.exe D:/尚硅谷Python/test.py16111621263136打印99乘法表for i in ran…

基于EasyUI的Web應用程序及過去一年的總結

前言 一個多月之前已經提交了離職申請&#xff0c;好在領導都已經批準了&#xff0c;過幾天就辦理手續了&#xff0c;在此感謝領導的栽培與挽留&#xff0c;感謝各位同事在工作中的給我的幫助&#xff0c;離開這個團隊確實有一些不舍&#xff0c;不為別的&#xff0c;只因為這個…

MySQL外鍵創建失敗1005原因總結

1、安裝mysql有InnoDB的插件擴展 ./configure --prefix/usr/local/mysql --with-pluginscsv,innobase,myisam,heap,innodb_plugin 2、找不到主表中 引用的列 3、主鍵和外鍵的字符編碼不一致 4、外鍵字段與要做外鍵校驗的字段類型不匹配 5、MySQL支持外鍵約束&#xff0c;并…

Hibernate的事件機制

4.8 事 件 機 制 通常&#xff0c;Hibernate執行持久化過程中&#xff0c;應用程序無法參與其中。所有的數據持久化操作&#xff0c;對用戶都是透明的&#xff0c;用戶無法插入自己的動作。 通過事件框架&#xff0c;Hibernate允許應用程序能響應特定的內部事件&#xff0c;從而…

快速使用Vue3最新的15個常用API

之前我寫了一篇博客介紹了Vue3的新特性&#xff0c;簡單了解了一下Vue3都有哪些特色&#xff0c;并且在文末帶大家稍微體驗了一下Vue3中 Compsition API 的簡單使用上一篇文章地址&#xff1a;緊跟尤大的腳步提前體驗Vue3新特性&#xff0c;你不會還沒了解過Vue3吧因為這個月的…