2011-04-12 93 views
0

我已經清理並縮短了多用戶遊戲的代碼片段,以顯示我想要完成的操作。所以在這裏,它是:正確處理跨線程共享類的鎖定方式

public class Subject { 
     public List<IObject> Objects = new List<IObject>(); 
    } 

    public interface IOpenable 
    { 
     void Open(Subject by, params string[] p); 
     void Close(Subject by, params string[] p); 
     bool IsOpen { get; } 
    } 

    public interface IObject { 
     void Pickup(Subject by, params string[] p); 
     void Drop(Subject by, params string[] p); 
    } 

    public class Box : IObject, IOpenable 
    { 
     bool opened = false; 
     Subject owner = null; 

     public void Pickup(Subject subject, params string[] p) { 
      subject.Objects.Add(this); 
      this.owner = subject; 
     } 

     public void Drop(Subject subject, params string[] p) 
     { 
      subject.Objects.Remove(this); 
      this.owner = null; 
     } 

     public void Open(Subject by, params string[] p) 
     { 
      this.opened = true; 
     } 

     public void Close(Subject by, params string[] p) 
     { 
      this.opened = false; 
     } 

     public bool IsOpen { get { return opened; } } 
    } 

我想知道的是: 如何防止某些用戶(從另一個線程執行代碼)來打開當前正在由其他一些用戶拿起一個盒子。 我已經想到了一些方法,但我認爲這裏的人經常想出聰明的想法,這可以讓我避免一些愚蠢的設計問題。

編輯:正如在回答提出,要使用鎖關鍵字在打開的方法:這是不是我想要什麼,我會嘗試用解釋是允許什麼,什麼不是:

網絡我們得到的請求以某種方式異步並且如果發佈得很快就會出錯。

  • (1)用戶1次發出指令PICKUP BOX
  • (2)用戶1次發出指令OPEN BOX
  • (3)用戶1所發出命令關閉BOX
  • (4)用戶2次發出指令OPEN BOX
  • (5)用戶2次發出指令LOOK BOX
  • (6)用戶1次發出指令打開框中

我們得到這個順序:

2,3,1,5,4,6

2 - allow 
3 - allow 
1 - allow [remains in execution and has not set the owner] 
5(comes in between 1) - allow 
4(comes in between 1) - disallow (not because already open but because 1 is in execution) 
6(comes in between 1) - allow since it is from user 1, and he is currently picking it up 

謝謝!

+0

我發佈將滿足新的條件解決方案 - TryOpen( )將在(3)執行時返回false。如果需要,您可以將相同的方法應用於其他方法以滿足進一步的條件。 – 2011-04-12 04:07:52

+0

在tcp中,據我所知,消息的順序是保證的,所以可能發生的最壞情況是1,3,4,2 - 這是你必須在你的代碼中處理的,意思是記住誰'擁有'哪個對象 – 2011-04-12 04:09:26

+0

到目前爲止正如我所看到的,如果有時候命令不同步,如果它們發出的速度非常快,似乎每個命令都有一個連接,而不是一個連接,但是不是我的代碼的一部分,我不是專家......:/ – 2011-04-12 04:16:06

回答

-1

我的這個解決方案是:

Monitor.TryEnter(obj) 

類似鎖,但不阻塞。

所以我們鎖定的對象,當用戶開始拿起它,

,並打開時,盒子做了檢查,說:

if (this.subject == subject || Monitor.TryEnter(obj)) 
+0

。這沒有任何意義。 – 2011-08-09 19:20:28

5

您可以使用lock statement來防止兩個線程訪問打開和關閉。爲防止race condition檢查框是否已打開,我們可以將Open()更改爲TryOpen(),如果該框已打開,則返回false。有辦法做到這一點,以便我們不必返回布爾值,但這可能是最簡單的。

如果一個線程到達鎖定語句,而另一個線程已經在鎖定語句中,則第二個線程將等待,直到第一個線程退出鎖定語句,然後再繼續。

private object locker=new object(); 
public bool TryOpen(Subject by, params string[] p) 
{ 
    lock(locker) 
    { 
     if(this.opened) 
      return false; 
     this.opened = true; 
     return true; 
    } 
} 

public void Close(Subject by, params string[] p) 
{ 
    lock(locker) 
    { 
     this.opened = false; 
    } 
} 
+0

我知道鎖定語句,順便說一句,它不是防止同時打開關閉,而是防止打開一個正在被另一個用戶拾取的盒子。不過,我認爲我還沒有很好地闡明自己。我將編輯帖子以反映這一點。 – 2011-04-12 03:58:51

+0

補充說明 – 2011-04-12 04:04:27

+0

您不斷變更規則! :)我不會改變我的答案,因爲您可能會添加新的規則 - 應該很容易遵循我向您展示的邏輯,以使其適用於您的特定情況。 – 2011-04-12 04:15:22