2017-06-23 25 views
0
join(user) { 
    if(this.room.length === 2) throw new Error('The room is full'); 
    this.room.push(user); 
}; 

如果異步性在'功能'的基礎上進行,這段代碼是100%防彈的。在一個房間裏不可能有兩個以上的用戶。是不同步a)函數或b)代碼行的常量?

如果「每行」出現異步,則此代碼可能會失敗。爲什麼?因爲如果三個用戶同時進入房間,以下情況可能會以10毫秒爲間隔發生:

1ms:房間爲空。讓用戶A進入數組。

4ms:房間有一個用戶。

5ms:用戶B要求加入。

6ms:用戶C要求加入。

7ms:檢查數組的長度(1)。

8ms:檢查數組的長度(1)。

9ms的:推送用戶B數組,因爲房間的長度爲1

10ms的:推送用戶C數組,因爲房間的長度爲1

15ms的:現在室有3個用戶和max爲2 。

如果異步性是以「每行」爲基礎的,如何避免前面的例子在真實場景中發生?對不起,如果我沒有叫他們的名字,但我想不出一個更好的方式來解釋這一點。

評論提示(但不能肯定地和明確地說)同一功能的「實例」不會彼此重疊。指向同一個對象/數組的不同功能呢?

join(user) { 
    if(GLOBAL.room1.length === 2) throw new Error('The room is full'); 
    GLOBAL.room1.push(user); 
}; 

join2(user) { 
    if(GLOBAL.room1.length === 2) throw new Error('The room is full'); 
    GLOBAL.room1.push(user); 
}; 
+2

由於您提到了node.js,因此它使用了單線程模型,這意味着您的函數將在您獲得對該函數的另一個調用之前完全執行(假設'this.room.push()'不是以某種方式重寫,以進入'join()'的遞歸調用)。一般來說,JavaScript並沒有很多需要鎖/ semapohres等的其他語言的併發性挑戰,以避免你所描述的情況。 – tavnab

+2

可能重複的[JavaScript是否保證單線程?](https://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded) – tavnab

+0

你基本上是描述共享多線程環境或[coroutines](http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/)。相反,JavaScript環境通常是事件驅動和單線程的。這有幫助嗎? – jib

回答

1

JavaScript是每個事件同步的(比函數或行更大的範圍)。這排除了您自己開始的任何異步操作。他們將開始並在一段時間後完成自己的事件(請參閱稍後在此討論事件)。事件的同步代碼(如您所展示的)將在下一個事件可以運行之前完全運行。

您在nodejs中的javascript以單線程和事件驅動的方式運行。這意味着一塊Javascript開始執行,它將在任何其他事件可以被處理之前結束。因此,一個函數在任何人都可以再次調用之前運行並完成(假設它沒有調用它自己),所以你沒有在多線程環境中可能存在的典型競態條件。

如果異步性是基於'功能'的基礎上的,這段代碼是100%防彈的。在一個房間裏不可能有兩個以上的用戶。

node.js中的Javascript是單線程的,它將在運行下一個事件之前運行整個事件處理程序以完成(該事件處理程序中的所有功能)。

如果異步性是以'每行'爲基礎的,則此代碼可能會失敗。爲什麼?因爲如果三個用戶同時進入房間,以下情況可能會以10毫秒爲間隔發生:

Javascript的單線程不是每行或每個函數。這是每個事件。因此,在這種情況下,您可能正在處理傳入的socket.io消息。這意味着該傳入消息事件處理程序中的所有代碼都將在處理任何其他傳入消息之前運行。只有當事件處理程序將控制權返回給系統時(通過從其事件處理函數返回),nodejs纔會獲取事件隊列中的下一個事件並調用其事件處理程序。

因此,假設三位用戶都要求大約在同一時間加入同一個房間,並檢查該情況以進一步解釋其工作原理。

  1. 這三個客戶都要求大約在同一時間加入同一個房間。
  2. 其中一個請求比另一個稍早到達(進入的數據包在網絡電纜和入站TCP堆棧中序列化,因此它們之一首先到達)。
  3. 該數據包由本地TCP堆棧處理,然後警告nodejs中的偵聽套接字(這將使用本機代碼並在與JS引擎不同的線程中運行)。
  4. nodejs平臺代碼獲取此套接字通知並將事件插入到nodejs事件隊列中,以提醒偵聽該傳入消息的nodejs代碼。
  5. 如果nodejs Javascript引擎此刻沒有做任何事情,那麼它立即啓動該事件處理程序並開始運行與該事件處理程序關聯的代碼。
  6. 如果nodejs Javascipt引擎此刻正在執行某些操作,那麼事件將處於事件隊列中,直到JS引擎完成當前正在執行的操作,然後它將獲取事件隊列中的下一個事件。
  7. 現在另外兩個加入同一個房間的請求到達TCP堆棧。
  8. 正在處理傳入TCP數據包的nodejs中的本機代碼,請參閱這兩個請求中的每一個請求。它們像第一個那樣插入到nodejs事件隊列中。由於JS解釋器正忙於爲到達的第一條消息提供服務,因此這些消息現在只是坐在事件隊列中。
  9. 當第一個消息完成其處理時,nodejs解釋器從事件隊列中獲取下一個事件(無論哪個消息到達第二個消息都將立即得到處理)。它的事件處理程序將被調用並在第三個事件處理之前運行完成。

希望從這個事情處理過程的描述中,你可以看到你的房間長度檢查沒有任何可能的競爭條件。將用戶添加到房間是單線程的,因此在任何其他加入房間的請求可以被處理之前它將開始,進行並完成。這種單線程特性(偶爾是一種限制特性)極大地簡化了nodejs編程,因爲它消除了多線程編程所帶來的競爭條件的許多可能原因。


讓我們來看看你的序列與我的每一步評論:

1ms的:房間是空的。讓用戶A進入數組。

4ms:房間有一個用戶。

5ms:用戶B要求加入。

如果第一個請求尚未完成處理,那麼這個請求將被放入事件隊列中,直到第一個請求完成。如果第一個請求完成,那麼這個請求將開始處理。

6ms:用戶C要求加入。

假設第二個請求需要超過1ms來處理,所以它還沒有完成,這個請求將進入事件隊列並且不會被處理,直到前兩個請求都完成完成。

7ms:檢查數組的長度(1)。

我不明白這一步是什麼意思。如果這是處理第二個請求的一部分,那麼第二個和第三個請求仍然在事件隊列中,並且只有在第一個請求完成時才能運行。

8ms:檢查數組的長度(1)。

與以前的評論相同。

9ms的:推送用戶B數組,因爲房間的長度爲1

10ms的:推送用戶C數組,因爲房間的長度爲1

15ms的:現在室有3個用戶和最大是兩個。

這是有缺陷的邏輯。由於nodejs Javascript的事件隊列和單線程性質,userB和userC請求在userA請求完成之前不會運行。因此,當任何請求運行時,房間長度總是準確的。像這樣的多個請求不會同時運行。

+0

@needitohelp - 這是解釋嗎?這回答了你的問題了嗎? – jfriend00

+0

說實話,沒有。這麼多段我沒有要求的信息,可能會混淆別人的信息而不是清理事情。我昨天又添加了一個問題:'指向同一個對象/數組的不同函數呢?',根據你的回答,如果他們是不同事件的一部分,他們可能會相互接觸,對嗎? – needitohelp

+0

@needitohelp - 每次運行一個活動。不能說比這更簡單。沒有兩個功能可以同時運行。 – jfriend00

相關問題