Correct way to handle locking of classes shared across threads
I have cleaned and shortened a snippet of my code for a multiuser game to show what I would like to accomplish. So here it is:
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; } }
}
What I would like to know is: How to prevent some user (executing code from another thread) to open a Box that is currently being Picked up by some other user. I've thought of some ways but I think that people here often come up with clever ideas, that could make me avoid some stupid design issue.
EDIT: As suggested in the answers, to use the lock keyword in the open method: this is not exactly what I want, I'll try to explain with what is allowed and what not:
The network requests we get as input are somehow async and get out of开发者_Python百科 order if issued fast.
- (1) User 1 issues command PICKUP BOX
- (2) User 1 issues command OPEN BOX
- (3) User 1 issues command CLOSE BOX
- (4) User 2 issues command OPEN BOX
- (5) User 2 issues command LOOK BOX
- (6) User 1 issues command OPEN BOX
We get this order:
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
Thanks!
You can use the lock statement to prevent two threads from accessing Open and Close. To prevent a race condition on checking if the box is already opened, we can change Open()
to TryOpen()
, and return false if the box is already open. There are ways to do this so that we don't have to return a boolean, but this is probably the simplest.
If one thread reaches the lock statement while another thread is already inside a lock statement, the second thread will wait until the first thread exits the lock statement before continuing.
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;
}
}
My solution to this is:
Monitor.TryEnter(obj)
Similar to lock but not blocking.
So we lock an object when a user start picking it up,
and when Opening the box do a check that says:
if (this.subject == subject || Monitor.TryEnter(obj))
精彩评论