#c# #multithreading
#c# #многопоточность
Вопрос:
Я очистил и сократил фрагмент моего кода для многопользовательской игры, чтобы показать, чего я хотел бы достичь. Итак, вот оно:
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; } }
}
Что я хотел бы знать, так это:
Как запретить какому-либо пользователю (выполняющему код из другого потока) открывать окно, которое в данный момент получает какой-либо другой пользователь.
Я думал о нескольких способах, но я думаю, что люди здесь часто выдвигают умные идеи, которые могли бы помочь мне избежать какой-нибудь глупой проблемы с дизайном.
РЕДАКТИРОВАТЬ: Как предложено в ответах, использовать ключевое слово lock в методе open: это не совсем то, что я хочу, я попытаюсь объяснить, что разрешено, а что нет:
Сетевые запросы, которые мы получаем в качестве входных данных, каким-то образом асинхронны и выходят из строя, если выполняются быстро.
- (1) Пользователь 1 выдает ОКНО ВВОДА команды
- (2) Пользователь 1 выдает команду ОТКРЫТЬ ОКНО
- (3) Пользователь 1 выдает команду ЗАКРЫТЬ ОКНО
- (4) Пользователь 2 выдает команду ОТКРЫТЬ ОКНО
- (5) Пользователь 2 выдает ОКНО ПОИСКА команд
- (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
Спасибо!
Комментарии:
1. Решение, которое я опубликовал, удовлетворит вашим новым условиям — TryOpen() вернет false при выполнении (3). Вы можете применить тот же подход к другим методам, удовлетворяющим дополнительным условиям, если требуется.
2. насколько я знаю, в tcp последовательность сообщений гарантируется, поэтому худшее, что может случиться, это 1,3,4,2 — это вы должны обработать в своем коде, то есть запомнить, кому какой объект «принадлежит»
3. насколько я видел, иногда команды не синхронизируются, если они выполняются очень быстро, похоже, что для каждой команды есть соединение, а не одно соединение, однако это не моя часть кода, с которой нужно возиться, я не эксперт… :/
4. @Greg не могли бы вы, пожалуйста, опубликовать пример того, каким должно быть содержимое метода получения и открытия? PS Я добавил промежуточное закрытие, и теперь я думаю, что tryopen пройдет, и не должен.
Ответ №1:
Вы можете использовать инструкцию lock, чтобы запретить двум потокам доступ к Open и Close. Чтобы предотвратить состояние гонки при проверке, открыто ли уже окно, мы можем изменить 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. Я знаю об операторе lock, и, кстати, он предназначен не для предотвращения одновременного открытия closing, а для предотвращения открытия окна, которое получает другой пользователь. Однако я думаю, что я недостаточно прояснил себя. Я отредактирую сообщение, чтобы отразить это.
2. Вы продолжаете менять правила! 🙂 Я не собираюсь менять свой ответ, потому что вы можете добавить новые правила — должно быть достаточно легко следовать логике, которую я показал вам, чтобы заставить ее работать в вашем конкретном случае.
3. хорошо, я думаю, что теперь я хорошо объяснил, чего я пытаюсь достичь, извините за путаницу. теперь со всеми 6 правилами все завершено.
4. нелегко хорошо объяснить, чего я хочу достичь, и английский — не мой язык… но, тем не менее, я поставил вам лайк за ваши усилия 🙂
Ответ №2:
Мое решение для этого:
Monitor.TryEnter(obj)
Похоже на блокировку, но не блокирует.
Таким образом, мы блокируем объект, когда пользователь начинает его использовать,
и при открытии окна сделайте проверку, которая гласит:
if (this.subject == subject || Monitor.TryEnter(obj))
Комментарии:
1. я думаю, это было результатом неправильного редактирования. Это не имело никакого смысла.