将Task.Run中的代码编组到调用线程上

时间:2019-04-07 06:10:51

标签: multithreading .net-core

我正在使用Task.Run将带有Socket的网络服务器分解为后台进程。我将Task存储在服务器类的一个字段中,以便将来在需要时可以取消它。 Task启动后,我将启动状态为同步运行的游戏引擎。

private void ListenForConnection()
{
    this.listeningTokenSource = new CancellationTokenSource();
    this.listeningTask = Task.Run(async () =>
    {
        while (!this.IsDeleted)
        {
            Socket clientSocket = await this.serverSocket.AcceptAsync();
            await this.CreatePlayerConnection(clientSocket);
        }
    }, this.listeningTokenSource.Token);
}

private async Task CreatePlayerConnection(Socket clientConnection)
{
    IServerConnection playerConnection = await this.connectionFactory.CreateConnection(clientConnection);
    IPlayer player = await this.playerFactory.CreatePlayer(playerConnection);
    if (playerConnection is TcpConnection tcpConnection)
    {
        tcpConnection.SetPlayer(player);
    }

    this.connectedPlayers.Add(player);
    await this.PlayerConnected(player);

    await player.ExecuteCommand(this.InitialCommand);
}

在这种情况下,如果播放器连接到服务器,则我将Socket传递到IServerConnection实例中,并将其分配给播放器。当客户端命令通过套接字发送时,该客户端的IServerConnection会对其进行解析,然后在管道中的某个位置将要修改该IServerConnection之外的游戏状态;通过与引擎内部运行的实体进行交互(甚至可以在另一个线程本身上进行交互)。

这一切都发生在netcoreapp2.0控制台应用程序中。

当客户端套接字进入处于不同线程的状态时,我是否会遇到来自于后台操作的客户端套接字问题?在WPF和Aspnet中,我过去使用过SynchronizationContext,但是控制台应用程序没有。

有什么干净的方法可以确保我不会到处死锁,而不必到处乱扔垃圾。我可以在启动ListenForConnection()任务之前将当前线程捕获,然后将封送状态更改回原来的线程吗?

1 个答案:

答案 0 :(得分:0)

从我的角度来看,您总是会得到一个公共资源,该资源将由不同的线程进行更新。因此,使用lock是可行的方法。但是,如果将资源和锁包装在一个对象中您可以将所有synchronization操作保留在本地。

public class Resource{
    public int Value{get;set;}
}

public class Engine
{
   private Resource commonResource=new Resource();
   private object @lock=new object();
   public void  ChangeResource(int newvalue)
   {
      lock(@lock)
      {
        this.commonResource.Value=newvalue;
      }
   }
}
public class Client{
    private Engine engine;
    public Client(Engine engine)
    {
        this.engine=engine;
    }
    public void ChangeResource(int newValue){
        this.engine.ChangeResource(newvalue);
    }
}

PS 我不知道您如何使用您的Engine / wrapper(无论您说客户端如何进行更改),所以我只是将其注入所有客户端中。您可以使用{{ 1}}。 如果您运行singleton操作,请考虑使用SempahoreSlim而不是锁。