2015-07-15 11 views
0

У меня есть сервер, настроенный для прослушивания на порте для клиентов, а затем, если они найдут один, добавьте их в клиентский массив и послушайте их из другого потока.Threading дает неправильный аргумент

Console.Write("Max Players: "); 
maxPlayers = Int32.Parse(Console.ReadLine()); 
clients = new TcpClient[maxPlayers]; 
playerCount = 0; 

formatter = new BinaryFormatter(); 
server = new TcpListener(IPAddress.Parse("192.168.1.33"), 7777); 
server.Start(); 

while (true) 
{ 
    if (server.Pending() && playerCount < maxPlayers) 
    { 
     Console.WriteLine("Found client"); 
     clients[playerCount] = server.AcceptTcpClient(); // Get client connection 
     //When one player joins, this should start a thread with an a playerCount of 0 
     Thread t = new Thread(() => ListenClient(playerCount)); 
     t.Start(); 
     playerCount++; 
    } 
} 

public static void ListenClient(int index) 
{ 
    while (true) 
    { 
     NetworkStream stream = clients[index].GetStream(); 
     object obj = formatter.Deserialize(stream); 

     if (obj != null) 
     { 
      Console.WriteLine(obj); 
     } 
    } 
} 

Однако, когда один игрок присоединяется, поток называется и передается аргумент 1, а не 0 по какой-то причине. В чем проблема?

+0

Threading, мой друг. кажется, как будто ссылки to player-Count увеличивается в главном потоке, прежде чем начнется ваш второй поток. Попробуйте поместить ключевое слово блокировки вокруг увеличения уровня воспроизведения. – icbytes

+2

@icbytes это не имеет ничего общего с потоками, это о том, как C# обрабатывает замыкания. – Andrey

+0

Закрытие? Как его обнаружить здесь? – icbytes

ответ

4

Ну, вот как работает лямбда. Попробуйте вместо этого:

while (true) 
{ 
    if (server.Pending() && playerCount < maxPlayers) 
    { 
     Console.WriteLine("Found client"); 
     clients[playerCount] = server.AcceptTcpClient(); // Get client connection 
     //When one player joins, this should start a thread with an a playerCount of 0 
     int currentPlayersCount = playerCount; 
     Thread t = new Thread(() => ListenClient(currentPlayersCount)); 
     t.Start(); 
     playerCount++; 
    } 
} 

EDIT:

Как это было принято в качестве ответа, следует отметить, что сообщения DavidG и Андрея ниже дают дополнительную важную информацию о затворов, и следует читать, чтобы получить полная картина.

+0

Если бы подобная проблема была однажды, это тоже решило это для меня. –

2

Альтернатива сообщению shay__ будет заключаться в использовании ParameterizedThreadStart.

Thread t = new Thread(new ParameterizedThreadStart((i) => ListenClient(i))); 
t.Start(playerCount); 
playerCount++; 
+1

Я этого не знал, спасибо! –

2

Проблема, которую вы видите здесь, связана с закрытием. Возьмите свой код выше, он переходит в переменную playerCount, а не значение этой переменной. последующие строки выполняются достаточно быстро, чтобы переменная увеличивалась до начала потока, поэтому значение равно 1. Чтобы решить эту проблему, вы можете скопировать значение локально в область действия блока if и передать это вместо этого:

int localPlayerCount = playerCount; 
Thread t = new Thread(() => ListenClient(localPlayerCount)); 

Дальнейшее чтение на затворах здесь: What are 'closures' in .NET?

+0

Сказал, что пропустил Закрытие. – icbytes

1

отвечу больше вопроса здесь, практическая часть отвечает @shay__.

Проблема здесь в неправильном использовании clojure. Закрытие (лямбда в этом случае) не просто получает копию значения переменной в момент создания, но и получает ссылку на переменную, поэтому, когда вы действительно получаете значение (звонок ListenClient, вы получаете значение в тот момент времени (который 1). Это не очень интуитивным к тому, что C# команда made a breaking change и фиксированной он (или сделано более интуитивным) в C 5,0 #.

http://csharpindepth.com/Articles/Chapter5/Closures.aspx

+1

Довольно многое, что мой ответ сказал (хотя я ссылался на закрытие, а не на Clojure, который является языком программирования!) – DavidG

+0

Я должен добавить что-то: Если лямбда + замыкания внутренне переводит пройденный тип значения параметра в класс и, следовательно, ссылочный тип, ПОЧЕМУ И ДЛЯ ЧТО Эта конструкция затворов эффективно полезна? Как говорит Андрей, это неинтуитивно, и смущает больше, а затем предлагает разумную возможность. На самом деле это (если не знать о случайном создании закрытия) действительно разрушает все распространенные чувства о программировании, не так ли? – icbytes

+0

@icbytes no, переданные параметры трактуются как простые значения, которые ничего не представляют.Закрытие создается только в том случае, если вы используете локальные переменные внешней функции, как если бы они были локальными переменными лямбда (или анонимными методами. Существует множество практических применений, Linq использует это в большой степени, например: «Где (x => x> y) Эта лямбда закрыта, потому что она захватывает y извне. Она не интуитивная, но в большинстве случаев она работает нормально. В действительности очень мало случаев, когда вы можете стрелять в ногу, как из вопроса, но я бы сказал, если вы возитесь с потоками, у вас должно быть твердое понимание. – Andrey

Смежные вопросы