C# 线程给出了错误的论点

C# 线程给出了错误的论点,c#,multithreading,C#,Multithreading,我设置了一个服务器来监听客户机的端口,如果他们找到了一个端口,就将其添加到客户机阵列中,并从那里的另一个线程监听它们 Console.Write("Max Players: "); maxPlayers = Int32.Parse(Console.ReadLine()); clients = new TcpClient[maxPlayers]; playerCount = 0; formatter = new BinaryFormatter(); server = new TcpListene

我设置了一个服务器来监听客户机的端口,如果他们找到了一个端口,就将其添加到客户机阵列中,并从那里的另一个线程监听它们

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);
        }
    }
}
Console.Write(“Max Players:”);
maxPlayers=Int32.Parse(Console.ReadLine());
clients=新的TcpClient[maxPlayers];
playerCount=0;
格式化程序=新的二进制格式化程序();
服务器=新的TcpListener(IPAddress.Parse(“192.168.1.33”),7777);
server.Start();
while(true)
{
if(server.Pending()&&playerCountListenClient(playerCount));
t、 Start();
playerCount++;
}
}
公共静态void ListenClient(int索引)
{
while(true)
{
NetworkStream=clients[index].GetStream();
object obj=格式化程序。反序列化(流);
如果(obj!=null)
{
控制台写入线(obj);
}
}
}

但是,当一个玩家加入时,线程会被调用并传递一个参数1,而不是出于某种原因传递给0。这里的问题是什么?

嗯,lambda就是这样工作的。 请尝试以下方法:

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++;
    }
}
while(true)
{
if(server.Pending()&&playerCountListenClient(CurrentPlayerCount));
t、 Start();
playerCount++;
}
}
编辑:


由于这是一个被接受的答案,应该提到的是,DavidG和Andrey在下面的帖子提供了关于闭包的额外重要信息,应该阅读这些帖子以获得完整的信息。

shay_uuu的答案的另一种选择是使用
参数化线程启动

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

您在这里看到的问题与闭包有关。以上面的代码为例,它传入变量
playerCount
,而不是该变量的值。后续行的运行速度足够快,因此在线程正确启动之前,变量会递增,因此值为1。要解决此问题,您可以将值本地复制到
if
块的范围中,并将其传递到内部:

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

关于闭包的进一步阅读:

我将在这里回答更大的问题,实践部分由@shay_uuuo回答

这里的问题是clojure的用法不正确。闭包(在本例中为lambda)不仅仅在创建时获取变量值的副本,它还获取变量的引用,因此当您实际获取值时(调用
ListenClient
您在该时刻获取值(即1)。对于C#团队和修复它(或使其更直观)来说,这不是非常直观的在C#5.0中


有一次遇到类似的问题,这也为我解决了。线程化,我的朋友。似乎在第二个线程开始之前,主线程中对玩家计数的引用增加了。尝试在玩家计数增加的周围放置一个lock关键字。@icbytes这与线程化无关,而是关于C#如何处理闭包。闭包?如何在这里检测它?说了那个,但错过了闭包。和我的回答差不多(虽然我引用了闭包,而不是Clojure,它是一种编程语言!)我必须补充一点:如果lambda+闭包在内部将传递的参数值类型转换为类,从而转换为引用类型,那么,为什么闭包的构造有效地有用?正如Andrey所说,这是不直观的,并且混淆了更多,然后提供了合理的可能性。事实上(如果没有意识到意外创建了闭包)真的破坏了编程的所有常识,不是吗?@icbytes不,传递的参数被视为简单的值没有什么特别的。只有当您使用外部函数的局部变量,就像它们是lambda的局部变量一样,才会创建闭包(或匿名方法。有无数的实际用法,Linq大量使用它,例如'.Where(x=>x>y)’。这个lambda是闭包,因为它从外部捕获y。它不是直观的,但在大多数情况下都可以正常工作。很少有情况下你可以像问题中那样射中自己的脚。但我想说,如果你在搞乱线程,你应该充分理解你关于搞乱线程的说法是正确的。但闭包只会增加这种情况下的复杂性,对吗?@icbytes有这样的原因。带闭包的lambda实际上非常方便。它们最初来自函数语言,其中变量是不可变的,因此传递它们总是安全的。许多命令式语言都有这个问题,Python也是如此普尔。