我应该锁定源代码的哪一部分?

时间:2019-05-28 12:48:30

标签: c# tcp thread-safety locking

以下是我的.net服务器的基本框架:

C# server skeleton

class ServerProgram
{
    static string origClientID = string.Empty;
    static string reqClientID = string.Empty;
    static string stKey = string.Empty;
    static string stValue = string.Empty;
    static Dictionary<string, KeyValue> KeyValueDictionary;
    static Dictionary<string, ClientClass> ClientDictionary;

    static void Main(string[] args)
    {
        Console.Title = "Server";
        Console.WriteLine("Server program started on address [" + Constants.SERVER_IP +":"+Constants.PORT_NO+"]");

        KeyValueDictionary = new Dictionary<string, KeyValue>();
        ClientDictionary = new Dictionary<string, ClientClass>();

        string ipAddress = Constants.SERVER_IP;
        int portNo = Constants.PORT_NO;

        IPAddress ip = IPAddress.Parse(ipAddress);            
        TcpListener listener = new TcpListener(ip, portNo);            

        // poll for clients in a 2nd thread
        Thread thread = new Thread(delegate()
        {
            ServerProgram.PollIncomingClientConns(listener);
        });

        thread.Start();
    }

    #region catching client connections
    static void PollIncomingClientConns(TcpListener listener)
    {
        listener.Start();

        try
        {
            bool keepRunning = true;

            while (keepRunning)
            {
                ClientClass client = new ClientClass(listener);

                ClientDictionary.Add(client.ID, client);

                Thread thread = new Thread(delegate()
                {
                    ServerProgram.ReadFromClient(client);
                });
                thread.Start();
            }
        }
        catch (Exception ex)
        {
            var inner = ex.InnerException as SocketException;
            if (inner != null && inner.SocketErrorCode == SocketError.ConnectionReset)
                Console.WriteLine("Disconnected");
            else
                Console.WriteLine(ex.Message);

            listener.Stop();
        }
    } 
    #endregion     

    static void ReadFromClient(ClientClass client)
    {
       try
        {
            while (client.Tcp.Connected)
            {
                string str = client.Read();
                Console.WriteLine("[" + client.ID + "] says: " + str);

                switch(str)
                {
                    case Commands.AddKeyValue:
                        //...                        
                        break;

                    case Commands.ListKeys:
                        //...
                        break;

                    case Commands.UpdateValue: 
                        //...
                        break;

                    case Commands.Yes:                            
                        //...
                        break;
                }
            }
        }
        catch
        {
            client.Disconnect();
        }
    }
}

该计划的目标是:

  1. 服务器保留一个通用的键值字典。
  2. 客户端可以在服务器中添加,更新和查看键值。

该类中使用了六个静态全局变量。

在访问之前我应该​​锁定其中的哪些(我认为词典不需要锁定)?

我应该锁定哪一部分代码?我应该分别锁定开关盒,还是应该将整个while循环锁定在static void ReadFromClient(ClientClass client)中?

1 个答案:

答案 0 :(得分:2)

我们可以肯定地说 ,您应该同步两个字典的访问-因为没有记录表明对字典的操作是线程安全的,并且您正在将它们从多个线程中进行变异。一些类似的结构 do 具有线程安全说明,但没有Dictionary<TKey,TValue>

是否需要同步字符串很棘手,尤其是因为您没有显示用法。对引用类型(string)的读写是原子的,字符串本身是不可变的(至少在正常使用时)。但是,如果您要执行多个操作,而它们之间的值保持不变,则可能需要在 composite 操作期间进行同步。此外,请记住,您不能只lock (stValue),因为当stValue更改时,两个不同的路径将锁定不同的对象。相反,您需要一个单独的字段,该字段仅用于锁定,并且与值无关。

最后,请注意,static字段-像这样可变时,通常是个坏主意。