c非同步編程
1. C#如何使用非同步編程
C#非同步方法:提供了一種簡便方式完成可能需要長時間運行的工作,而不必阻止調用方的線程。非同步方法的調用方可以繼續工作,而不必等待非同步方法完成。
代碼實現過程:
///<summary>
///非同步方法,解決長等待問題
///</summary>
///<paramname="action"></param>
///<paramname="entity"></param>
///<returns></returns>
publicasyncTaskAsyncAdd(Func<FAQ_Info,VMessage>fun,FAQ_Infoentity)
{
returnSystem.Threading.Tasks.Task.Run(()=>
//解決UI阻塞,這種並不是真正的並行執行,而是開了一個新線程非同步執行代碼段
//主UI不會等待它返回結果的,如果要與主線程合作開發,即並行開發,需要加await來獲取Task的返回結果
{
fun(entity);
});
}
非同步的三種模式:
1.等待模式,在發起了非同步方法以及做了一些其它處理之後,原始線程就中斷,並且等待非同步方法完成之後再繼續。
2.輪詢模式,原始線程定期檢查發起的線程是否完成,如果沒有則可以繼續做一些其它的事情。
3.回調模式,原始線程一直在執行,無需等待或檢查發起的線程是否完成。在發起的線程中的引用方法完成之後,發起的線程就會調用回調方法,由回調方法在調用EndInvoke之前處理非同步方法的結構。
2. javascript中非同步操作的異常怎麼處理
一、JavaScript非同步編程的兩個核心難點
非同步I/O、事件驅動使得單線程的JavaScript得以在不阻塞UI的情況下執行網路、文件訪問功能,且使之在後端實現了較高的性能。然而非同步風格也引來了一些麻煩,其中比較核心的問題是:
1、函數嵌套過深
JavaScript的非同步調用基於回調函數,當多個非同步事務多級依賴時,回調函數會形成多級的嵌套,代碼變成
金字塔型結構。這不僅使得代碼變難看難懂,更使得調試、重構的過程充滿風險。
2、異常處理
回調嵌套不僅僅是使代碼變得雜亂,也使得錯誤處理更復雜。這里主要講講異常處理。
二、異常處理
像很多時髦的語言一樣,JavaScript 也允許拋出異常,隨後再用一個try/catch
語句塊捕獲。如果拋出的異常未被捕獲,大多數JavaScript環境都會提供一個有用的堆棧軌跡。舉個例子,下面這段代碼由於'{'為無效JSON
對象而拋出異常。
?
12345678
function JSONToObject(jsonStr) { return JSON.parse(jsonStr);}var obj = JSONToObject('{');//SyntaxError: Unexpected end of input//at Object.parse (native)//at JSONToObject (/AsyncJS/stackTrace.js:2:15)//at Object.<anonymous> (/AsyncJS/stackTrace.js:4:11)
堆棧軌跡不僅告訴我們哪裡拋出了錯誤,而且說明了最初出錯的地方:第4 行代碼。遺憾的是,自頂向下地跟蹤非同步錯誤起源並不都這么直截了當。
非同步編程中可能拋出錯誤的情況有兩種:回調函數錯誤、非同步函數錯誤。
1、回調函數錯誤
如果從非同步回調中拋出錯誤,會發生什麼事?讓我們先來做個測試。
?
1234567
setTimeout(function A() { setTimeout(function B() { setTimeout(function C() { throw new Error('Something terrible has happened!'); }, 0); }, 0);}, 0);
上述應用的結果是一條極其簡短的堆棧軌跡。
?
12
Error: Something terrible has happened!at Timer.C (/AsyncJS/nestedErrors.js:4:13)
等等,A 和B 發生了什麼事?為什麼它們沒有出現在堆棧軌跡中?這是因為運行C 的時候,非同步函數的上下文已經不存在了,A 和B 並不在內存堆棧里。這3
個函數都是從事件隊列直接運行的。基於同樣的理由,利用try/catch
語句塊並不能捕獲從非同步回調中拋出的錯誤。另外回調函數中的return也失去了意義。
?
1234567
try { setTimeout(function() { throw new Error('Catch me if you can!'); }, 0);} catch (e) {console.error(e);}
看到這里的問題了嗎?這里的try/catch 語句塊只捕獲setTimeout函數自身內部發生的那些錯誤。因為setTimeout
非同步地運行其回調,所以即使延時設置為0,回調拋出的錯誤也會直接流向應用程序。
總的來說,取用非同步回調的函數即使包裝上try/catch 語句塊,也只是無用之舉。(特例是,該非同步函數確實是在同步地做某些事且容易出錯。例如,Node
的fs.watch(file,callback)就是這樣一個函數,它在目標文件不存在時會拋出一個錯誤。)正因為此,Node.js
中的回調幾乎總是接受一個錯誤作為其首個參數,這樣就允許回調自己來決定如何處理這個錯誤。
2、非同步函數錯誤
由於非同步函數是立刻返回的,非同步事務中發生的錯誤是無法通過try-catch來捕捉的,只能採用由調用方提供錯誤處理回調的方案來解決。
例如Node中常見的function (err, ...)
{...}回調函數,就是Node中處理錯誤的約定:即將錯誤作為回調函數的第一個實參返回。再比如HTML5中FileReader對象的onerror函數,會被用於處理非同步讀取文件過程中的錯誤。
舉個例子,下面這個Node 應用嘗試非同步地讀取一個文件,還負責記錄下任何錯誤(如「文件不存在」)。
?
1234567
var fs = require('fs'); fs.readFile('fhgwgdz.txt', function(err, data) { if (err) { return console.error(err); }; console.log(data.toString('utf8'));});
客戶端JavaScript 庫的一致性要稍微差些,不過最常見的模式是,針對成敗這兩種情形各規定一個單獨的回調。jQuery 的Ajax
方法就遵循了這個模式。
?
1234
$.get('/data', { success: successHandler, failure: failureHandler});
不管API 形態像什麼,始終要記住的是,只能在回調內部處理源於回調的非同步錯誤。
三、未捕獲異常的處理
如果是從回調中拋出異常的,則由那個調用了回調的人負責捕獲該異常。但如果異常從未被捕獲,又會怎麼樣?這時,不同的JavaScript環境有著不同的游戲規則……
1. 在瀏覽器環境中
現代瀏覽器會在開發人員控制台顯示那些未捕獲的異常,接著返回事件隊列。要想修改這種行為,可以給window.onerror
附加一個處理器。如果windows.onerror 處理器返回true,則能阻止瀏覽器的默認錯誤處理行為。
?
123
window.onerror = function(err) { return true; //徹底忽略所有錯誤};
在成品應用中, 會考慮某種JavaScript 錯誤處理服務, 譬如Errorception。Errorception
提供了一個現成的windows.onerror 處理器,它向應用伺服器報告所有未捕獲的異常,接著應用伺服器發送消息通知我們。
2. 在Node.js 環境中
在Node 環境中,window.onerror 的類似物就是process 對象的uncaughtException 事件。正常情況下,Node
應用會因未捕獲的異常而立即退出。但只要至少還有一個uncaughtException 事件處理
器,Node 應用就會直接返回事件隊列。
?
123
process.on('uncaughtException', function(err) { console.error(err); //避免了關停的命運!});
但是,自Node 0.8.4 起,uncaughtException 事件就被廢棄了。據其文檔所言,對異常處理而言,uncaughtException
是一種非常粗暴的機制,請勿使用uncaughtException,而應使用Domain 對象。
Domain 對象又是什麼?你可能會這樣問。Domain 對象是事件化對象,它將throw 轉化為'error'事件。下面是一個例子。
?
123456789
var myDomain = require('domain').create();myDomain.run(function() { setTimeout(function() { throw new Error('Listen to me!') }, 50);});myDomain.on('error', function(err) { console.log('Error ignored!');});
源於延時事件的throw 只是簡單地觸發了Domain 對象的錯誤處理器。
Error ignored!
很奇妙,是不是?Domain 對象讓throw
語句生動了很多。不管在瀏覽器端還是伺服器端,全局的異常處理器都應被視作最後一根救命稻草。請僅在調試時才使用它。
四、幾種解決方案
下面對幾種解決方案的討論主要集中於上面提到的兩個核心問題上,當然也會考慮其他方面的因素來評判其優缺點。
1、Async.js
首先是Node中非常著名的Async.js,這個庫能夠在Node中展露頭角,恐怕也得歸功於Node統一的錯誤處理約定。
而在前端,一開始並沒有形成這么統一的約定,因此使用Async.js的話可能需要對現有的庫進行封裝。
Async.js的其實就是給回調函數的幾種常見使用模式加了一層包裝。比如我們需要三個前後依賴的非同步操作,採用純回調函數寫法如下:
?
12345678910111213141516
asyncOpA(a, b, (err, result) => { if (err) { handleErrorA(err); } asyncOpB(c, result, (err, result) => { if (err) { handleErrorB(err); } asyncOpB(d, result, (err, result) => { if (err) { handlerErrorC(err); } finalOp(result); }); });});
如果我們採用async庫來做:
?async.waterfall([ (cb) => { asyncOpA(a, b, (err, result) => { cb(err, c, result); }); }, (c, lastResult, cb) => { asyncOpB(c, lastResult, (err, result) => { cb(err, d, result); }) }, (d, lastResult, cb) => { asyncOpC(d, lastResult, (err, result) => { cb(err, result); }); }], (err, finalResult) => { if (err) { handlerError(err); } finalOp(finalResult);});
可以看到,回調函數由原來的橫向發展轉變為縱向發展,同時錯誤被統一傳遞到最後的處理函數中。
其原理是,將函數數組中的後一個函數包裝後作為前一個函數的末參數cb傳入,同時要求:
每一個函數都應當執行其cb參數;cb的第一個參數用來傳遞錯誤。我們可以自己寫一個async.waterfall的實現:
?let async = { waterfall: (methods, finalCb = _emptyFunction) => { if (!_isArray(methods)) { return finalCb(new Error('First argument to waterfall must be an array of functions')); } if (!methods.length) { return finalCb(); } function wrap(n) { if (n === methods.length) { return finalCb; } return function (err, ...args) { if (err) { return finalCb(err); } methods[n](...args, wrap(n + 1)); } } wrap(0)(false); }};
Async.js還有series/parallel/whilst等多種流程式控制制方法,來實現常見的非同步協作。
Async.js的問題:
在外在上依然沒有擺脫回調函數,只是將其從橫向發展變為縱向,還是需要程序員熟練非同步回調風格。
錯誤處理上仍然沒有利用上try-catch和throw,依賴於「回調函數的第一個參數用來傳遞錯誤」這樣的一個約定。
2、Promise方案
ES6的Promise來源於Promise/A+。使用Promise來進行非同步流程式控制制,有幾個需要注意的問題,
把前面提到的功能用Promise來實現,需要先包裝非同步函數,使之能返回一個Promise:
?
12345678910
function toPromiseStyle(fn) { return (...args) => { return new Promise((resolve, reject) => { fn(...args, (err, result) => { if (err) reject(err); resolve(result); }) }); };}
這個函數可以把符合下述規則的非同步函數轉換為返回Promise的函數:
回調函數的第一個參數用於傳遞錯誤,第二個參數用於傳遞正常的結果。接著就可以進行操作了:
?
123456789101112131415
let [opA, opB, opC] = [asyncOpA, asyncOpB, asyncOpC].map((fn) => toPromiseStyle(fn)); opA(a, b) .then((res) => { return opB(c, res); }) .then((res) => { return opC(d, res); }) .then((res) => { return finalOp(res); }) .catch((err) => { handleError(err); });
通過Promise,原來明顯的非同步回調函數風格顯得更像同步編程風格,我們只需要使用then方法將結果傳遞下去即可,同時return也有了相應的意義:
在每一個then的onFullfilled函數(以及onRejected)里的return,都會為下一個then的onFullfilled函數(以及onRejected)的參數設定好值。
如此一來,return、try-catch/throw都可以使用了,但catch是以方法的形式出現,還是不盡如人意。
3、Generator方案
ES6引入的Generator可以理解為可在運行中轉移控制權給其他代碼,並在需要的時候返回繼續執行的函數。利用Generator可以實現協程的功能。
將Generator與Promise結合,可以進一步將非同步代碼轉化為同步風格:
?
1234567891011
function* getResult() { let res, a, b, c, d; try { res = yield opA(a, b); res = yield opB(c, res); res = yield opC(d); return res; } catch (err) { return handleError(err); }}
然而我們還需要一個可以自動運行Generator的函數:
?
2324252627282930
function spawn(genF, ...args) { return new Promise((resolve, reject) => { let gen = genF(...args); function next(fn) { try { let r = fn(); if (r.done) { resolve(r.value); } Promise.resolve(r.value) .then((v) => { next(() => { return gen.next(v); }); }).catch((err) => { next(() => { return gen.throw(err); }) }); } catch (err) { reject(err); } } next(() => { return gen.next(undefined); }); });}
用這個函數來調用Generator即可:
?
1234567
spawn(getResult) .then((res) => { finalOp(res); }) .catch((err) => { handleFinalOpError(err); });
可見try-catch和return實際上已經以其原本面貌回到了代碼中,在代碼形式上也已經看不到非同步風格的痕跡。
類似的功能有co/task.js等庫實現。
4、ES7的async/await
ES7中將會引入async function和await關鍵字,利用這個功能,我們可以輕松寫出同步風格的代碼,
同時依然可以利用原有的非同步I/O機制。
採用async function,我們可以將之前的代碼寫成這樣:
?
12345678910111213
async function getResult() { let res, a, b, c, d; try { res = await opA(a, b); res = await opB(c, res); res = await opC(d); return res; } catch (err) { return handleError(err); }} getResult();
和Generator & Promise方案看起來沒有太大區別,只是關鍵字換了換。
實際上async
function就是對Generator方案的一個官方認可,將之作為語言內置功能。
async function的缺點:
await只能在async function內部使用,因此一旦你寫了幾個async function,或者使用了依賴於async
function的庫,那你很可能會需要更多的async function。
目前處於提案階段的async
function還沒有得到任何瀏覽器或Node.JS/io.js的支持。Babel轉碼器也需要打開實驗選項,並且對於不支持Generator的瀏覽器來說,還需要引進一層厚厚的regenerator
runtime,想在前端生產環境得到應用還需要時間。
以上就是本文的全部內容,希望對大家的學習有所幫助。
3. 如何寫一個「非同步函數」
我們平常編程寫的函數 幾乎都是同步調用函數,那麼我們如何寫一個非同步執行的函數呢?!我想這個問題也許是哪些比較喜歡專研的程序員或者具有專研精神的人士回提出的問題吧!我們很多人已經習慣了windows系統提供的一些非同步機制,使用這些非同步機制我們很快的就能實現一些非同步操作甚至可以很容易的實現一個非同步執行的函數;但是我們研究過實現一個「非同步函數」的本質嗎?! 在單線程的系統中,所以的指令執行都是順序執行的,這就暗示了如果一個函數A中調用了函數B,則A必須等到B執行後才能繼續執行A中剩下的代碼。 在多線程中,如果我們有一個threadA線程,在該線程中調用了一個函數C,而該C函數我們想將它實現成非同步執行的,而非同步執行必須要有多線程支持;如果我們在Windows中編寫程序,創建一個線程是很簡單只要使用 HANDLE WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId); 函數就可以創建一個線程。 那麼我們按如下方式可以實現一個非同步的FuncC函數: (1)先把你要非同步完成的工作單獨寫成要給函數,如 DWORD WINAPI AsyncronousThread( LPVOID lpParameter // thread data){ .....}(2)在函數FuncC中使用CreateThtread函數將(1)中的函數創建一成一個線程,然後直接返回。 CreateThread(....,AsyncronousThread,...);return;}當然,寫一個非同步函數的方法很多,但是一個本質不會變,就是必須要依據多線程才能實現。
4. PIC單片機16F883 串列非同步通信 c語言編程 請詳細寫出各個寄存器,串口的作用,謝謝了。
#include <pic.h>
#define FOSC 18432000L
#define BAUD 115200
#define NONE_PARITY 0 //無校驗位
#define ODD_PARITY 1 //奇校驗
#define EVEN_PARITY 2 //偶校驗
#define MARK_PARITY 3 //標記校驗
#define SPACE_PARITY 4 //空校驗
#define PARITYBIT EVEN_PARITY
#define S2RI 0x01
#define S2TI 0x02
#define S2RB8 0x04
#define S2TB8 0x08
sfr AUXR = 0x8e;
sfr S2CON = 0x9a;
sfr S2BUF = 0x9b;
sfr BRT = 0x9c;
sfr IE2 = 0xaf;
bit busy;
void SendData(char dat);
void SendString(char *s);
void main()
{
#if (PARITYBIT == NONE_PARITY)
S2CON = 0x5a; //8位可變波特率 (無校驗位)
#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
S2CON = 0xda; //9位可變波特率,校驗位初始為1
#elif (PARITYBIT == SPACE_PARITY)
S2CON = 0xd5; //9位可變波特率,校驗位初始為0
#endif
BRT = -(FOSC/32/BAUD); //設置獨立波特率發生器的重載初值
AUXR = 0x14; //獨立波特率發生器工作在1T模式
IE2 = 0x01; //使能串口2中斷
EA = 1; //開總中斷
SendString("STC12C5A60S2\r\nUart2 Test !\r\n");
while(1);
}
void Uart2() interrupt 8 using 1
{
if (S2CON & S2RI)
{
S2CON &= ~S2RI; //清除接收完成標志
P0 = S2BUF; //P0顯示串口數據
P2 = (S2CON & S2RB8); //P2.2顯示校驗位
}
if (S2CON & S2TI)
{
S2CON &= ~S2TI; //清除發送完成標志
busy = 0;
}
}
void SendData(char dat)
{
while (busy); //等待上個數據發送完成
ACC = dat; //取得偶校驗位P
if (P) //根據P來設置串口數據的校驗位
{
#if (PARITYBIT == ODD_PARITY)
S2CON &= ~S2TB8; //置校驗位為0
#elif (PARITYBIT == EVEN_PARITY)
S2CON |= S2TB8; //置校驗位為1
#endif
}
else
{
#if (PARITYBIT == ODD_PARITY)
S2CON |= S2TB8; //置校驗位為1
#elif (PARITYBIT == EVEN_PARITY)
S2CON &= ~S2TB8; //置校驗位為0
#endif
}
busy = 1;
S2BUF = ACC; //發送數據
}
void SendString(char *s)
{
while (*s) //判斷字元串結束標志
{
SendData(*s++); //發送字元
}
}
5. 請問linux下C編程多線程同步和非同步的區別,如何能實現程序的同步和非同步編程
同步就是使得兩個或者多個進程之間的行為按照一定的時序來執行。比如說線程A完成了某件事,然後線程B才能做某件事。具體一點,就是,線程間的某個動作執行前需要確認一個或者多個其他線程的當前狀態。而非同步則是多個線程各跑各的,互不幹涉。
Linux下的多線程實現由pthread庫提供,頭文件為pthread.h。多線程最重要的就是要保護好共享資源(用互斥體,mutex),尤其是非同步。代碼哥哥就不上了,這里關鍵的不是代碼的問題,也不是Linux、Windows的問題,重要的是概念的理解。哥們不妨先研究研究「生產者-消費者」這個常出現在教科書上的模型,這是一個典型的同步問題。就講這么多了,拜拜。
6. C#幾種非同步編程
1、非同步編程模型 (APM) 模式(也稱為 IAsyncResult 模式),其中非同步操作要求 Begin 和 End 方法(例如,非同步寫操作的 BeginWrite 和 EndWrite)。對於新的開發工作不再建議採用此模式。
2、基於事件的非同步模式 (EAP) 需要一個具有 Async 後綴的方法,還需要一個或多個事件、事件處理程序、委託類型和 EventArg 派生的類型。EAP 是在 .NET Framework 2.0 版中引入的。對於新的開發工作不再建議採用此模式。
3、基於任務的非同步模式 (TAP),該模式使用一個方法表示非同步操作的啟動和完成。.NET Framework 4 中引入了 TAP,並且是 .NET Framework 中非同步編程的建議方
7. 如何正確理解.NET 4.5和C#5.0中的async/await非同步編程模式
相對於之前Begin/End模式和事件模式,async/await模式讓程序員得以用同步的代碼結構進行非同步編程。async/await入門很方便,但是深入理解卻涉及很多領域,如線程池、同步上下文等等。我斷斷續續接觸了幾個月,稍微有一些心得:
await的作用是等待非同步Task完成,並不是阻塞的。舉個例子,一個非同步方法:
publicasyncTaskCaller()
{
Action0();
awaitMethod();
Action3();
}
publicasyncTaskMethod()
{
Action1();
awaitTask.Delay(1000);
Action2();
}
A.當你在非UI線程A上執行Caller(),將完成以下幾件事:
[線程A]執行Action0()
[線程A]調用await Method()
[線程A]執行Action1()
[線程A]啟動任務Task.Delay(1000),並在線程池裡安插一個新任務,在Task.Delay(1000)完成後,由另一個線程執行6
[線程A]去處理別的事情
[線程B]執行Action2()
[線程B]await Method()返回
[線程B]執行Action3()
其中,線程A和線程B並不保證是同一個線程。如果你在await前後列印線程ID,你會發現ID是不同的。
B.當你在UI線程上執行Caller(),過程有了變化:
[UI線程]執行Action0()
[UI線程]調用await Method()
[UI線程]執行Action1()
[UI線程]啟動任務Task.Delay(1000),並在線程池裡安插一個新任務,在Task.Delay(1000)完成後,由另一個線程執行6
[UI線程]去處理別的事情
[線程C]在UI線程的同步上下文中執行7(類似於在窗體類上執行Invoke()方法)
[UI線程]執行Action2()
[UI線程]await Method()返回
[UI線程]執行Action3()
可見,當使用await方法的線程為UI線程時,程序默認會通過第6步,保證await前後是同一個線程ID。這個當然是有一定性能犧牲的(甚至會造成死鎖,在D里會討論),如果你不想在await完成後回到UI線程,見C。
C. 你可以在UI線程上使用await XXX().ConfigureAwait(false)去替代awaitXXX(),來禁止當await XXX()結束時恢復線程。舉個例子,執行下列代碼是沒問題的(如B里描述的):
privateasyncvoidbutton1_Click(objectsender,EventArgse)
{
this.Text="123";
awaitTask.Delay(1000);
this.Text="321";
}
但是,執行下列代碼就會發生「線程間操作無效」的錯誤:
privateasyncvoidbutton1_Click(objectsender,EventArgse)
{
this.Text="123";
awaitTask.Delay(1000).ConfitureAwait(false);
this.Text="321";//線程間操作無效
}
因為執行
this.Text="321";
的線程已經不再是UI線程。
D. 順便一提,Task.Wait()方法,相比於await Task,會同步地執行Task。但是,如果你在UI線程上Wait的Task里本身又有await,那麼將會產生死鎖:
privatevoidFoo(objectsender,EventArgse)
{
this.Text="123";
Method().Wait();//此處發生死鎖
this.Text="321";//這行永遠也不會執行
}
privateasyncTaskMethod()
{
awaitTask.Delay(1000);
}
為什麼呢?Method().Wait()會阻塞UI線程等待Method()完成,但是參照B過程,在await完成後,Method()完成前,是需要恢復到UI線程的,但是此時UI線程已經被阻塞了,因此死鎖就發生了。
要避免這個死鎖,可以參照C。
E. 說出來你可能不信,上面的都是我手打的。在內容上雖然不一定嚴謹,但希望對樓主和其它新接觸TAP的朋友有一定啟發。
8. [求助]關於window phone 的非同步編程
[mw_shl_code=csharp,true]public long ComputeSomething(int a, int b) { Thread.Sleep(5000); return 100; } private void Button_Click(object sender, RoutedEventArgs e) { Dispatcher.BeginInvoke(delegate { Console.WriteLine("請輸入文件夾名稱(例如:C:\\Windows):"); long size = ComputeSomething(1, 2); Console.WriteLine("正在計算中,請耐心等待……"); Console.WriteLine("\n計算完成。文件夾的容量為:{0}位元組\n", size); }); }[/mw_shl_code]試試!
9. 如何進行nodejs非同步編程
更新下,我之所以讓您玩一下AJAX,是希望您體驗一下非同步,並不是希望您了解AJAX這機制的實現方法,因為AJAX是一個特別典型且簡單的非同步場景,比如:
執
行某個函數 -> 執行語句A,B,C,D -> 在D語句發起非同步請求,同時向引擎注冊一個回調事件 -> 執行E,F,G
->退出函數塊 ,引擎Loop...Loop...Loop,此時非同步的請求得到了Response,之前注冊的回調被執行。
@VILIC VANE
也提到了,實際上Node.js主要是為了應對主流web
app存在大量I/O等待而CPU閑置的場景所衍生的解決方案,而在架構上,它的後端有一個底層的worker封裝,每當你有一個諸如addUser這樣
的I/O操作時,它們都會被交由worker去執行從而達到讓出盡快讓出當前函數的執行權的目的,在向引擎注冊完回調後,內部會通過事件輪詢去檢查該I
/O事件的句柄,當句柄顯示該事件操作完成後,則注冊的回調則被執行。
所以,假設有人(按題設,簡化一下場景,有且只有2個人)同時請求
addUser(A)和userList(B),B的請求會在執行完A的請求內部所有同步代碼後被執行,而哪怕worker此時仍然在進行addUser
這一 I/O操作,用戶B也並不會被引擎掛起或者等待。這就是為什麼Node.js單節點卻一樣可以擁有高負載能力的原因。
至於什麼樣的代碼是非同步的,你看看node文檔里fs模塊的使用方法就知道了,大概的形式就是如下這種。
mole.method( args [,callback] )
當然還有一種比較極端的情況,假設您使用的資料庫是山寨的,驅動是基於同步實現的,那麼B就該等多久等多久把,樹蔭底下喝杯茶,下個棋,和後面的C,D,E,F,G打個招呼唄~
我推薦您先去玩一下前端的AJAX了解一下 非同步編程方式,體驗一下非同步的「感覺」,然後看一本叫《JavaScript非同步編程》的書。
Node.js
是一款基於Event-driven的模型構建的Framework,它典型的特徵就是通過內置的事件輪詢來調度事件,通常來說node.js的資料庫驅
動都是基於非同步實現的,所以在實際情況中,A提交博客和B注冊用戶這兩個請求是可以同時由Node.js
來handle,並按照實際操作的處理事件分別調度給予瀏覽器響應。
當然,假設您在業務代碼里寫了一個耗時很久的同步代碼(比如直接寫一
個while(true)的loop,Node就死了),由於JavaScript本身單線程的限制,所以整個App就會被block住,後續的事件/程
序只有等到該段代碼執行完成之後才會被處理,這也是為什麼我們通常不建議在Node.js層做大規模計算(JS本身的計算效率太低,會導致Node吞吐量
會大大降低),而傾向由C++的拓展去實現。
10. C#中udp非同步編程之煩惱
這個非同步編程模型基本上就是掌握Begin函數和End函數就好了。
首先調用Begin,送一個委託(其實就是你自己寫的一個函數)進去當參數,其他幾個參數和同步的沒什麼太大區別。好了就沒你什麼事了……同步的時候此時是在等對吧,非同步的就沒得等了,函數直接返回。同步的時候函數返回就可以開始處理數據了對吧?非同步的時候要等你送進去當參數的那個函數被調用的時候才能開始處理數據。
等到數據來了或者異常發生了,它會調用你那個函數,把IAsyncResult介面的某個對象當作參數送進來。此時你調用End那個函數,就可以拿到結果了(如果接收過程中有異常發生,End函數會拋出那個異常)