如何同步线程,使它们响应用户输入?



学校给我布置了一个猜谜游戏的程序。玩家/用户连接到server,开始游戏并猜测一个单词。如果他猜对了单词,他就赢了。多个玩家/用户可以加入游戏(稍后我会限制在一个合理的数字,如3)。客户端通过putty连接

在这个任务中,我需要使用多个threads,然后以某种方式同步它们。让我们考虑有3个玩家在玩。第一个玩家猜测,输入他的猜测,我希望其他两个玩家等待玩家输入他的猜测。然后第二个玩家可以猜,然后第二个玩家可以猜第三个玩家,以此类推。

我的初步猜测a)我该怎么做?我使用了Join,但我不知道它是否完成了我想要的

b)这种游戏风格(等待玩家下一次猜测)对线程工作的例子有好处吗?

服务器类:

/// <summary>
/// Constuctor for creating server for players. 
/// 
/// </summary>
/// <param name="port">
/// Port for connecting to the server
/// 
/// </param>
public Server(int port)
{
myServer = new TcpListener(IPAddress.Any, port);
myServer.Start();
isRunning = true;
LoopServer();
}
/// <summary>
/// Every time a new user connects to the server, a new thread is created.
/// </summary>
private void LoopServer()
{
while (isRunning)
{
TcpClient client = myServer.AcceptTcpClient();
Thread thread = new Thread(new ParameterizedThreadStart(LoopClient));
thread.Start(client);
thread.Join();
}
}
/// <summary>
/// Method where is game played
/// </summary>
/// <param name="obj"></param>
private void LoopClient(object obj)
{

try
{
TcpClient client = (TcpClient)obj;
StreamReader reader = new StreamReader(client.GetStream(), Encoding.UTF8);
StreamWriter writer = new StreamWriter(client.GetStream(), Encoding.UTF8);
users.Add(new User(Thread.CurrentThread.ManagedThreadId));
writer.Flush();
puzzle.guessWord( writer, reader,users );
Console.WriteLine("Out of the server");

}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

游戏类:

/// <summary>
/// Everytime player/user take guess, the strign is added to list of user's gusses
/// </summary>
/// <param name="user">
/// </param>
/// <param name="userGuess"></param>
/// <returns>
/// Returns what is user's guess(string) and who took the guess(id)
/// </returns>
public string TakeGuess(User user, string userGuess)
{
user.Guesses.Add(Validation(userGuess));
return "Your guess:" + userGuess + " " + "player with id:" + user.Id;
}
/// <summary>
/// Here is the method in which the game itself is played.
/// </summary>
/// <param name="writer"></param>
/// <param name="reader"></param>
/// <param name="users"></param>
public void guessWord(StreamWriter writer, StreamReader reader, List<User> users)
{
Console.Write("Connected");
bool gameIsPlaying = true;
//Creates new User. The player's id indicates which thread the player is using
User user = new User(Thread.CurrentThread.ManagedThreadId);
writer.WriteLine("Guessing game");
Console.WriteLine("Number of players in game:" + " "+  users.Count);
if (users.Count > 0)
{
while (gameIsPlaying)
{
string data = reader.ReadLine();
//Whatever what writer object print, shows on client side(in putty)
writer.WriteLine(TakeGuess(user, data));
if (Validation(data).Equals(this.Word))
{
gameIsPlaying = false;
Console.WriteLine("Winner is user with id" + " " + user.Id);
writer.WriteLine("Winner is user with id"+ " " + user.Id);
users.Clear();
}

}
writer.WriteLine("The game is over");
}
}

我已经尝试同步线程(我在上面的文本中描述的盘/用户)

这是你们的作业,所以我给你们一些提示:

users.Add(new User(Thread.CurrentThread.ManagedThreadId));
writer.Flush();

我假设usersList<User>类型。该类型不是线程安全的,因此在访问它时需要一个锁,否则如果多个用户同时登录,该行为将未定义。

puzzle.guessWord( writer, reader, users );

在这里,您应该转发当前用户,而不是用户列表(无论如何,该列表在此上下文中是全局的)。

//Creates new User. The player's id indicates which thread the player is using
User user = new User(Thread.CurrentThread.ManagedThreadId);

这是错误的。您已经为这个线程创建了用户,所以使用它吧。参数中给出的用户(见上文)是当前用户。

while (gameIsPlaying)
{
string data = reader.ReadLine();
//Whatever what writer object print, shows on client side(in putty)
writer.WriteLine(TakeGuess(user, data));
if (Validation(data).Equals(this.Word))
{
gameIsPlaying = false;
// ...
}
}

这里您需要确定该轮到谁。因此,保留一个带有下一个用户ID的成员变量(注意,Game类只有一个实例,但它运行许多线程)。用锁保护该变量。在循环的每次迭代中,检查是否轮到我们。如果是,请猜测。如果没有,输出"Please wait for player XY",等一会儿再试一次。当然,您也可以做一些更复杂的事情,比如使用信号量。这将允许用户在获得锁时进行猜测。

我将List<User>集合更改为更安全的ConcurrentQueue

ConcurrentQueue<User> users = new ConcurrentQueue<User>();

我已经初始化了信号量

static Semaphore semaphore = new Semaphore(1,Environment.ProcessorCount);

方法guessWord()是这样的

if (users.Count > 0)
{

while (gameIsPlaying)
{
string data = reader.ReadLine();
//Whatever what writer object print, shows on client side(in putty)
writer.WriteLine(TakeGuess(user, data));
if(winner != 0)
{
Console.WriteLine("Number of players in game:" + " " + users.Count);
gameIsPlaying = false;
Console.WriteLine("Winner is user with id" + " " + winner);
writer.WriteLine("Winner is user with id" + " " + winner);
users.Clear();
}

if (Validation(data).Equals(this.Word) )
{
if (winner == 0)
{
winner = user.Id;
}
gameIsPlaying = false;
}


}

writer.WriteLine("The game is over");

方法LoopClient()classServer我添加了这三行来"启动交通灯">

semaphore.WaitOne();
puzzle.guessWord( writer, reader,users );
semaphore.Release();
Console.WriteLine("Now you are out of the game");