正确的方法来处理跨线程共享的类的锁定

时间:2011-04-12 03:38:07

标签: c# multithreading

我为多用户游戏清理并缩短了我的代码片段,以显示我想要完成的内容。所以这就是:

    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; } }
    }

我想知道的是: 如何防止某些用户(从另一个线程执行代码)打开当前被其他用户选中的Box。 我想过一些方法,但我认为这里的人经常提出聪明的想法,这可以让我避免一些愚蠢的设计问题。

编辑:正如答案中所建议的那样,在open方法中使用lock关键字:这不是我想要的,我会尝试解释什么是允许的,什么不是:

作为输入的网络请求以某种方式异步并且如果快速发出则会出现故障。

  • (1)用户1发出命令PICKUP BOX
  • (2)用户1发出命令OPEN BOX
  • (3)用户1发出命令CLOSE BOX
  • (4)用户2发出命令OPEN BOX
  • (5)用户2发出命令LOOK BOX
  • (6)用户1发出命令OPEN BOX

我们收到此订单:

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

谢谢!

2 个答案:

答案 0 :(得分:5)

您可以使用lock statement阻止两个线程访问“打开”和“关闭”。要阻止race condition检查框是否已打开,我们可以将Open()更改为TryOpen(),如果框已经打开,则返回false。有办法做到这一点,所以我们不必返回布尔值,但这可能是最简单的。

如果一个线程到达lock语句而另一个线程已经在lock语句中,则第二个线程将等到第一个线程退出lock语句后再继续。

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;
    }
}

答案 1 :(得分:-1)

我的解决方案是:

Monitor.TryEnter(obj)

类似于锁定而非阻止。

因此我们在用户开始拾取对象时锁定对象,

打开包装盒时,请执行以下检查:

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