亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > AJAX > 正文

揭秘在AJAX程序中實現互斥

2024-09-01 08:26:30
字體:
來源:轉載
供稿:網友

 隨著AJAX范例得到越來越廣泛的應用,瀏覽器頁面可以在向后臺服務器請求數據的同時保持前端用戶界面的活躍性(因此在AJAX中稱為異步)。然而,當這兩個活動同時訪問共用的JavaScript和DOM數據結構時就會引發問題。JavaScript沒有提供針對該并發程序問題的經典解決方案。本文描述了作者在互斥機制方面的新見解,該經過驗證的互斥機制在JavaScript中能發揮良好的作用。

  為什么需要互斥?

  當多個程序邏輯線程同時訪問相同數據的時候,問題便產生了。程序通常假定與其交互的數據在交互過程中不發生改變。訪問這些共享數據結構的代碼稱為臨界區,一次只允許一個程序訪問的機制被稱為互斥。在AJAX應用程序中,當對來自XMLHttpRequest的應答進行異步處理的代碼同時操縱正在被用戶界面使用的數據時,便會發生這種情況。這個共用的數據可能是用于實現MVC數據模型的JavaScript和/或web頁面自身的DOM。如果二者中的任一個對共享數據做了不協調的更改,那么二者的邏輯都將中斷。

  也許您會說“等等,為什么我沒有遇到過這種問題?”。遺憾的是,這種問題是同步依賴的(也叫做競態條件),因此它們并不總是發生,或者也許從不發生。它們的或然性基于許多因素?;诮研钥紤],富internet應用程序應該通過確保這些問題不會發生來阻止出現這種情況。

  因此,需要一種互斥機制來確保同時只能打開一個臨界區,并且在它結束之后才能打開另一個。在大多數主流計算機語言和執行框架中,都提供互斥機制(經常是幾種),但是應用于瀏覽器端的JavaScript卻沒有提供這種互斥機制。雖然存在一些無需專門的語言或環境支持的經典互斥實現算法,但是即使這樣還是需要一些JavaScript和瀏覽器(如Internet Explorer)所缺少的要素。接下來介紹的經典算法在這些瀏覽器和語言中能發揮良好的作用。

  面包店算法

  在計算機科學文獻中的幾種互斥算法中,所謂的Lamport面包店算法可以有效地用于多個相互競爭的控制線程,該算法中線程之間的通信只能在共享內存中進行(即,不需要諸如信號量、原子性的set-and-test之類的專門機制)。該算法的基本思想源于面包店,因為面包店需要先取號然后等候叫號。清單1給出了該算法的框架(引自Wikipedia),該算法可以使各線程進出臨界區而不產生沖突。

  清單1. Lamport面包店算法偽代碼

 

// declaration & initial values of global variables
Enter, Number: array [1..N] of integer = {0};

// logic used by each thread...
// where "(a, b) < (c, d)"
// means "(a < c) or ((a == c) and (b < d))"
Thread(i) {
 while (true) {
  Enter [i] = 1;
  Number[i] = 1 + max(Number[1],...,Number[N]);
  Enter [i] = 0;
  for (j=1; j<=N; ++j) {
   while (Enter[j] != 0) {
    // wait until thread j receives its number
   }
   while ((Number[j]!=0) && ((Number[j],j) < (Number[i],i))) {
    // wait until threads with smaller numbers
    // or with the same number, but with higher
    // priority, finish their work
   }
  }
  // critical section...
  Number[i] = 0;
  // non-critical section...
 }
}


  如上所示,該算法假定各線程清楚自己的線程編號(常量i)和當前正在活動的線程總數(常量N)。此外,還假定存在一種等待或休眠方式,例如:暫時將CPU釋放給其他線程。遺憾的是,Internet Explorer中的JavaScript沒有這種能力。雖然如此,如果實際運行在同一線程上的多個代碼部分表現為各自運行在獨立的虛擬線程上,那么該面包店算法不會中斷。同樣,JavaScript具有一種在指定延遲后調度函數的機制,所以,可以使用下面的這些方法來優化面包店算法。


  Wallace變體

  在JavaScript中實現Lamport面包店算法的主要障礙在于缺少線程API。無法確定當前正在哪個線程上運行以及當前正在活動的線程數目,也無法將CPU釋放給其他的線程,無法創建新的線程來管理其他線程。因此,無法查證如何將特定的瀏覽器事件(例如:單擊按紐、可用的XML應答等)分配到線程。

  克服這些障礙的一種方法是使用Command設計模式。通過將所有應該進入臨界區的邏輯以及所有啟動該邏輯所需的數據一起放入到command 對象中,可以在負責管理command的類中重寫面包店算法。該互斥類僅在沒有其他臨界區(封裝為獨立的command對象方法)在執行時調用臨界區,就像它們各自運行在不同的虛擬線程中一樣。JavaScript的setTimeout()機制用于將CPU釋放給其他正在等待的command。

  為command對象假定一個簡單的基類(見清單2中的Command),可以定義一個類(見清單3中的Mutex)來實現面包店算法的Wallace變體。注意,雖然可以通過很多方式在JavaScript中實現基類對象(為了簡潔起見,這里使用一種簡單的方式),但是只要各個command對象擁有某個惟一的id,而且整個臨界區被封裝在單獨的方法中,那么任何對象模式都可以使用這種方法。

  清單2. 用于 Command 對象的簡單基類

 

1 function Command() {
2  if (!Command.NextID) Command.NextID = 0;
3  this.id = ++Command.NextID;
4  // unsynchronized API
5  this.doit = function(){ alert("DOIT called"); }
6  this.undo = function(){ alert("UNDO called"); }
7  this.redo = function(){ this.doit(); }
8  // synchronized API
9  this.sDoIt = function(){ new Mutex(this,"doit"); }
10  this.sUnDo = function(){ new Mutex(this,"undo"); }
11  this.sReDo = function(){ new Mutex(this,"redo"); }
12 }


  Command類演示了三個臨界區方法(見5-7行),但是只要預先將對該方法的調用封裝在Mutex中(見9-11行),那么就可以使用任何方法。有必要認識到,常規方法調用(例如非同步的方法調用)與同步方法調用之間存在著重要的區別:具有諷刺意味的是,必須保證同步方法不同步運行。換句話說,當調用sDoIt()方法時,必須確保方法doit()還未運行,即使方法sDoIt()已經返回。doit()方法可能已結束,或者直到將來的某一時間才開始執行。也就是說,將對Mutex的實例化視為啟動一個新的線程。

  清單3.作為類 Mutex實現的 Wallace 變體

 

1 function Mutex( cmdObject, methodName ) {
2  // define static field and method
3  if (!Mutex.Wait) Mutex.Wait = new Map();
4   Mutex.SLICE = function( cmdID, startID ) {
5    Mutex.Wait.get(cmdID).attempt( Mutex.Wait.get(startID) );
6   }
7   // define instance method
8   this.attempt = function( start ) {
9    for (var j=start; j; j=Mutex.Wait.next(j.c.id)) {
10     if (j.enter
11      || (j.number && (j.number < this.number ||
12      (j.number == this.number
13      && j.c.id < this.c.id))))
14      return setTimeout
15      ("Mutex.SLICE("+this.c.id+","+j.c.id+")",10);
16     }
17     //run with exclusive access
18     this.c[ this.methodID ]();
19     //release exclusive access
20     this.number = 0;
21     Mutex.Wait.remove( this.c.id );
22    }
23    // constructor logic
24    this.c = cmdObject;
25    this.methodID = methodName;
26    //(enter and number are "false" here)
27    Mutex.Wait.add( this.c.id, this );
28    this.enter = true;
29    this.number = (new Date()).getTime();
30    this.enter = false;
31   this.attempt( Mutex.Wait.first() );
32  }


  Mutex類的基本邏輯是將每個新的Mutex實例放入主等待清單,然后將其在等待隊列中啟動。因為每次到達“隊首”的嘗試都需要等待(除了最后一次),所以使用setTimeout來調度每次在當前嘗試停止的位置啟動的新嘗試。到達隊首時(見17行),便實現了互斥性訪問;因此,可以調用臨界區方法。執行完臨界區后,釋放互斥性訪問并從等待清單中移除Mutex實例(見20-21行)。

  Mutex構造函數(見23-31行)記錄其Command對象和方法名參數,然后寄存在一個運行中臨界區的稀疏數組中(Mutex.Wait),這通過清單4中所示的Map類來實現。然后構造函數獲得下一個編號,并在隊尾開始排隊。由于等待編號中的間隔或副本不存在問題,所以實際上使用當前的時間戳作為下一個編號。

  attempt()方法將初始偽代碼中的兩個wait循環組合成一個單獨的循環,該循環直到隊首時才對臨界區失效。該循環是一種忙碌-等待循環檢測方式,可以通過在setTimeout()調用中指定延遲量來終止該循環。由于setTimeout需要調用“無格式函數”,所以在第4-6行定義了靜態幫助器方法(Mutex.SLICE)。SLICE在主等待清單中查找指定的Mutex對象,然后調用其attempt()方法,用start參數指定到目前為止其所獲得的等待清單的長度。每次SLICE()調用都像獲得了“一塊CPU”。這種(通過setTimeout)適時釋放CPU的協作方式令人想到協同程序。

  清單4. 作為 Map數據結構實現的稀疏數組

 

function Map() {
 this.map = new Object();
 // Map API
 this.add = function( k,o ){
  this.map[k] = o;
 }
 this.remove = function( k ){
  delete this.map[k];
 }
 this.get = function( k ){
  return k==null ? null : this.map[k];
 }
 this.first = function(){
  return this.get( this.nextKey() );
 }
 this.next = function( k ){
  return this.get( this.nextKey(k) );
 }
 this.nextKey = function( k ){
  for (i in this.map) {
   if ( !k ) return i;
   if (k==i) k=null; /*tricky*/
  }
  return null;
 }
}

 

  富Internet應用程序集成

  由于Mutex所處理的線程(虛擬的或者非虛擬的)數量是動態變化的,所以可以確定一個基本事實:無法通過像瀏覽器為各個瀏覽器事件分配單獨的線程那樣的方式來獲得線程標識符。這里做了一個類似的假定,那就是每個完整的事件處理程序組成一個完整的臨界區?;谶@些假定,每個事件處理函數都可以轉變成一個command對象,并使用Mutex對其進行管理。當然,如果未將代碼明確組織成事件處理函數,那么將需要重構。換句話說,不是直接在HTML事件屬性中進行邏輯編碼(例如:onclick='++var'),而是調用事件處理函數(例如:onclick='FOO()'和function FOO(){++var;})。

  清單5. 使用了非同步事件處理程序的示例web頁面

 

<html>
<script language="JavaScript">
function newState(){
 if (XMLreq.readyState==4) processReply();
}
function requestData(){
 ...set up asynchronous XML request...
 XMLreq.onreadystatechange = newState;
 ...launch XML request...
}
function processReply(){
 var transformedData = ...process data to HTML...
 OutputArea.innerHTML = transformedData + "<br>";
}
function clearArea(){
 OutputArea.innerHTML = "cleared<br>";
}
</script>
<body onload="requestData();">
?。糹nput type="button" value="clear" onclick="clearArea()">
?。糳iv id="OutputArea"/>
</body>
</html>


  例如,假設有三個事件處理程序函數,它們操縱清單5所示的共用數據。它們處理頁面加載事件、單擊按鈕事件和來自XML請求的應答事件。頁面加載事件發出某個異步請求來要求獲取數據并指定請求-應答事件處理程序,該處理程序處理接收到的數據,并將其加載到共用數據結構。單擊按鈕事件處理程序也影響共用數據結構。為了避免這些事件處理程序發生沖突,可以通過清單6所示的Mutex將它們轉變成command并加以調用(假設JavaScript include文件mutex.js中包含Map和Mutex)。注意,雖然可以使用優美的類繼承機制來實現Command子類,但是該代碼說明了最簡單的方法,該方法僅需要全局變量NEXT_CMD_ID。

  清單6. 轉化為同步事件處理程序的web頁面

 

<html>
<script src="mutex.js"></script>
<script language="JavaScript">
function requestData (){
 new Mutex(new RequestDataCmd(),"go"); }
 function processReply(){
  new Mutex(new ProcessReplyCmd(),"go"); }
 function clearArea (){
  new Mutex(new ClearAreaCmd(),"go"); }
 function newState (){
  if (XMLreq.readyState==4) processReply(); }

 var NEXT_CMD_ID = 0;

 function RequestDataCmd(){
  this.id = ++NEXT_CMD_ID;
  this.go = function(){
   ...set up asynchronous XML request...
   XMLreq.onreadystatechange = NewState;
   ...launch XML request...
  }
 }
 function ProcessReplyCmd(){
  this.id = ++NEXT_CMD_ID;
  this.go = function(){
   var transformedData = ...process data to HTML...
   OutputArea.innerHTML = transformedData + "<br>";
  }
 }
 function ClearAreaCmd(){
  this.id = ++NEXT_CMD_ID;
  this.go = function(){
   OutputArea.innerHTML = "cleared<br>"; }
 }
</script>
<body onload="requestData();">
<input type="button" value="clear" onclick="clearArea()">
<div id="OutputArea"/>
</body>
</html>


  已經通過Mutex將這三個事件處理程序函數轉變為調用它們的初始邏輯(當前都被預包裝于command類中)。各個command類定義一個獨特的標識符和一個包含臨界區邏輯的方法,從而滿足了command接口的要求。

  結束語

  借助于AJAX和RIA,構建復雜的動態用戶界面的推動力正在促使開發人員使用先前與胖GUI客戶端緊密聯系的設計模式(例如:模型-視圖-控制器)。隨著視圖和控制器的定義模塊化,且每一個都帶有自己的事件和事件處理程序(除了共用數據模型),發生沖突的機率成倍提高。通過把事件處理邏輯封裝到Command類中,不僅可以使用Wallace變體,而且為提供豐富的撤消/重做功能、腳本編寫界面和單元測試工具創造了條件。

 

 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美成人一区二区在线电影| 久久久久久美女| 日日狠狠久久偷偷四色综合免费| 久久久精品久久| 91成品人片a无限观看| 亚洲欧美精品中文字幕在线| 色婷婷**av毛片一区| 日本精品中文字幕| 色婷婷av一区二区三区在线观看| 欧美中文字幕视频在线观看| 亚洲人成网站999久久久综合| 日韩av综合网站| 91av在线播放视频| 国产激情视频一区| 欧美精品在线免费播放| 日韩一区二区久久久| 黑人巨大精品欧美一区二区| 中文字幕在线视频日韩| 国产视频福利一区| 欧美国产一区二区三区| 亚洲色图日韩av| 欧美日韩激情网| 成人在线视频福利| 欧美亚洲另类制服自拍| 久久精品成人一区二区三区| 成人福利网站在线观看11| 日韩欧美在线免费观看| 国产精品久久一区主播| 欧美激情网站在线观看| 福利视频导航一区| 97色在线播放视频| 日韩欧美在线字幕| 欧美激情一级欧美精品| 97精品国产aⅴ7777| 亚洲福利影片在线| 亚洲天堂免费视频| 欧美成人免费播放| 国产91精品高潮白浆喷水| 国产91ⅴ在线精品免费观看| 亚洲欧美一区二区激情| 亚洲美女又黄又爽在线观看| 国产欧美亚洲视频| 日韩少妇与小伙激情| 亚洲精品中文字幕有码专区| 欧美日韩国产色视频| 国产精品视频白浆免费视频| 一区二区三区视频免费在线观看| 97在线看免费观看视频在线观看| 欧美孕妇孕交黑巨大网站| 黑人巨大精品欧美一区免费视频| 国产精品天天狠天天看| 欧美在线影院在线视频| 国产一区二区在线免费视频| 91精品免费久久久久久久久| 国产精品久久久久久婷婷天堂| 久久艹在线视频| 九九精品视频在线| 欧美亚洲成人精品| 高清一区二区三区四区五区| 国产成人精品免费久久久久| 久久久精品亚洲| 欧洲日韩成人av| 91中文在线观看| 久久久91精品国产| 性色av一区二区三区在线观看| 91精品国产777在线观看| 国产视频精品免费播放| 91精品国产免费久久久久久| 亚洲成av人乱码色午夜| 欧美激情一区二区三区高清视频| 欧美国产欧美亚洲国产日韩mv天天看完整| 黑人极品videos精品欧美裸| 欧美疯狂xxxx大交乱88av| 日日狠狠久久偷偷四色综合免费| 久久久国产一区二区| 日韩大陆欧美高清视频区| 色综合久久精品亚洲国产| 亚洲午夜色婷婷在线| 久久精品久久久久| 日韩av高清不卡| 91精品国产综合久久久久久久久| 国产日韩欧美视频在线| 在线激情影院一区| 日本亚洲欧美成人| 亚洲成人av片在线观看| 中文字幕日韩av综合精品| 亚洲九九九在线观看| 九九久久国产精品| 成人免费在线视频网址| 综合久久五月天| 亚洲一区二区三区视频播放| 欧美国产日韩一区| 成人精品视频99在线观看免费| 激情成人中文字幕| 国产经典一区二区| 国产精选久久久久久| 91精品国产综合久久香蕉的用户体验| 4444欧美成人kkkk| 色天天综合狠狠色| 国产精品福利片| 欧美性猛交xxxx久久久| 中文字幕亚洲综合久久筱田步美| 日韩va亚洲va欧洲va国产| 亚洲精品欧美日韩专区| 欧美激情视频一区二区| 亚洲视频第一页| 国产精品久久久久久搜索| 日本视频久久久| 成人在线中文字幕| 欧美日韩国产综合视频在线观看中文| 久久99精品久久久久久青青91| 欧美日韩国产第一页| 精品国产网站地址| 欧美性生交xxxxxdddd| 丝袜美腿亚洲一区二区| 久久久久国产视频| 亚洲aⅴ男人的天堂在线观看| 欧美精品在线播放| 欧美国产日韩一区二区在线观看| 国产婷婷成人久久av免费高清| 欧美在线观看网站| 亚洲精品久久久久| 欧美最猛黑人xxxx黑人猛叫黄| 国产精品久久精品| 91日韩在线播放| 亚洲91精品在线| 亚洲成色999久久网站| 亚洲人成77777在线观看网| 国产精品美女主播在线观看纯欲| 国产精品久久激情| 97久久久久久| 成人xxxx视频| 91精品国产免费久久久久久| 国产精品久久久久久久久粉嫩av| 色香阁99久久精品久久久| 欧美成人久久久| 精品伊人久久97| 色悠久久久久综合先锋影音下载| 亚洲精品成人久久电影| 欧美成人精品不卡视频在线观看| 欧美激情一级二级| 蜜臀久久99精品久久久久久宅男| 亚洲人成电影网站色| 日韩在线视频网站| 国产精品视频26uuu| 久久精品影视伊人网| 国产精品视频免费在线观看| 精品亚洲aⅴ在线观看| 一区二区福利视频| 国产视频亚洲视频| 日韩av中文字幕在线免费观看| 欧美性xxxxx极品| 亚洲黄色av网站| 在线播放国产一区中文字幕剧情欧美| 亚洲成人激情在线| 亚洲国产精品久久91精品| 国产精品视频永久免费播放| 日韩av在线精品| 亚洲网站在线看| 日韩中文av在线| 亚洲成色777777在线观看影院| 日韩精品在线播放| 成人午夜在线影院| 久久色精品视频|