2011-04-12 42 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