Socket Server, locking and slowdowns

ertaboy356b

Old School Gamer
Reaction score
86
So creating this monitoring application is a really big challenge for me. Not only that it requires a large amount of database interaction, but it also needs to be realtime. The establishment I'm working on should have at least 500 active employees using the program at the same time which means a lot of stress to the suppose socket server I'm working.

The challenge is how to make the socket server thread-safe and at the same time fast. The socket sending issue has been resolve and I am now facing some "collections changed" problems due to multi-threading. I've implemented locking but that makes the program slow since it has to wait for certain threads to stop using the collections.

So if I create a new instance of the object like for example:
Code:
List<UserData> exclusiveUserDataList;
lock (UserDataList) {
  exclusiveUserDataList = UserDataList.ToList();
}
foreach (var userData in exclusiveUserDataList) {
  // Do Something
  lock(UserDataList) UserDataList.Remove(userData);
}
will it be better than doing this? :
Code:
lock (UserDataList) {
foreach (var userData in UserDataList) {
  // Do Something
   UserDataList.Remove(userData);
}
}

I feel like I should really change how the code works or else I'll be dead -_-

Might be important info: the program has at least 50 packet handlers (which runs functions depending on the packetType received asynchronously).
 

monoVertex

I'm back!
Reaction score
460
So what happens if some other thread / process / whatever you have changes the UserDataList? For example:

Code:
List<UserData> exclusiveUserDataList;
lock (UserDataList) {
  exclusiveUserDataList = UserDataList.ToList();
}
 
// Something occurs here: for example the UserDataList is emptied, or even worse, deleted.
// You'll get an exception or some unforeseen consequences in the for.
 
foreach (var userData in exclusiveUserDataList) {
  // Do Something
  lock(UserDataList) UserDataList.Remove(userData);
}

However, I need more information. It really depends in what does Do Something mean. I mean, when you copy the list, if there's Object in there, as far as I know the copying is shallow, so it won't copy the Objects as well, it'll only copy the references. If someone deletes the object inside, you'll no longer have that.

Provide more context and I'll be able to help you more.

Also, what exactly is UserDataList? Is it a class or a variable? If it's a variable why does it starts with capital U?...

EDIT: If you can provide the larger picture, as what exactly is done with the list in the other processes, we might be able to find another planning for your activities with the list, maybe make it faster. When using locks on a variable modified by a lot of processes and those modifications are the entire purpose of the module, then you're basically serializing all the stuff and you could just as well use a single process, you didn't gain anything. Some things can't be parallel, there's that too :p.
 

ertaboy356b

Old School Gamer
Reaction score
86
UserDataList is an ObservableCollection. It's actually a class-wide variable.
ObservableCollection<UserData> UserDataList;

UserData is this:
Code:
    public class UserData : PropertyChangedBase
    {
        private int _userId;
        public int UserId
        {
            get { return _userId; }
            set
            {
                _userId = value;
                NotifyOfPropertyChange(() => UserId);
            }
        }
 
        private string _userCode;
        public string UserCode
        {
            get { return _userCode; }
            set
            {
                _userCode = value;
                NotifyOfPropertyChange(() => UserCode);
            }
        }
 
        private int _projectId;
        public int ProjectId
        {
            get { return _projectId; }
            set
            {
                _projectId = value;
                NotifyOfPropertyChange(() => ProjectId);
            }
        }
 
        private string _projectName;
        public string ProjectName
        {
            get { return _projectName; }
            set
            {
                _projectName = value;
                NotifyOfPropertyChange(() => ProjectName);
            }
        }
 
        private string _position;
        public string Position
        {
            get { return _position; }
            set
            {
                _position = value;
                NotifyOfPropertyChange(() => Position);
            }
        }
 
        private bool _isNightShift;
        public bool IsNightShift
        {
            get { return _isNightShift; }
            set
            {
                _isNightShift = value;
                NotifyOfPropertyChange(() => IsNightShift);
            }
        }
 
        private int _rights;
        public int Rights
        {
            get { return _rights; }
            set
            {
                _rights = value;
                NotifyOfPropertyChange(() => Rights);
            }
        }
 
        private Connection _connection;
        public Connection Connection
        {
            get { return _connection; }
            set
            {
                _connection = value;
                NotifyOfPropertyChange(() => Connection);
            }
        }
 
        private string _ipAddress;
        public string IPAddress
        {
            get { return _ipAddress; }
            set
            {
                _ipAddress = value;
                NotifyOfPropertyChange(() => IPAddress);
            }
        }
 
        private int _port;
        public int Port
        {
            get { return _port; }
            set
            {
                _port = value;
                NotifyOfPropertyChange(() => Port);
            }
        }
 
        private bool _onQuery;
        public bool OnQuery
        {
            get { return _onQuery; }
            set
            {
                _onQuery = value;
                NotifyOfPropertyChange(() => OnQuery);
            }
        }
 
        private int _loginId;
        public int LoginId
        {
            get { return _loginId; }
            set
            {
                _loginId = value;
                NotifyOfPropertyChange(() => LoginId);
            }
        }
 
        private int _dropCount;
        public int DropCount
        {
            get { return _dropCount; }
            set
            {
                _dropCount = value;
                NotifyOfPropertyChange(() => DropCount);
            }
        }
    }

'Connection' is an object with socket features like for example SendObject().

I'll give a sample code:

Code:
private void Login(Connection connection, UserLoginData message) {
    var userIp = connection.IPAddress;
    var userPort = connection.Port;
    var data = new UserData()
    {
        UserCode = message.UserCode,
        UserId = message.UserId,
        ProjectId = message.ProjectId,
        ProjectName = message.ProjectName,
        // more variables
    };
    lock (UserDataList) UserDataList.Add(data);
    Console.WriteLine(data.UserCode + " has Logged in!");
   
    // Notify Staff
    List<UserData> dataList;
    lock (UserDataList) dataList = UserDataList.Where(p => p.rights > 0).ToList();
    // Send Message to Staffs
    for (var i = 0; i < dataList.Count; i++) SendObject(dataList[i], "Login", message);
}
 
private void Logout(Connection connection, UserLogoutData message) {
    UserData remove;
    List<UserData> userDataList;
    lock(UserDataList) userDataList = UserDataList.Where(p => p.UserId == message.UserId).Where(p => p.Connection == connection).ToList();
    for (var i = 0; i < userDataList.Count; i++)
    {
        remove = userDataList[i];
        Console.WriteLine("{0} has Logged Out!", userDataList[i].UserCode);
    }
   
    // Remove User
    lock(UserDataList) UserDataList.Remove(remove);
   
    // Notify Staffs
    lock(UserDataList) userDataList = UserDataList.Where(p => p.Position.ToLower() == "staff").ToList();
    // Send Messages to staffs
    for(var i = 0; i < userDataList.Count; i++) SendObject(userDataList[i], "Logout", message);
}
 
 
// Here's the code that let's the staff sends a message to all projects
private void ProjectMessage(Connection connection, SendProjectMessageData message)
{
    List<UserData> userDataList;
    lock(UserDataList) userDataList = UserDataList.Where(p => (p.ProjectId == message.ToProjectId) || (message.ToProjectId == 0)).ToList();
    for (var i = 0; i < userDataList.Count; i++) SendObject(userDataList[i], "ProjectMessage", message);
}
 
 
// And some members I used in this example
private readonly int _maxSocketDropCount = 10;
 
private void SendObject(UserData u, string packetType, object message)
{
    Task.Factory.StartNew(() =>
    {
    for (var i = 0; i < _maxSocketDropCount; i++)
    {
        try
        {
        u.Connection.SendObject(packetType, message);
        return;
        //Console.WriteLine("{0} has been notified", u.UserCode);
        }
        catch (Exception ex)
        {
        Console.WriteLine(ex.Message + " Resending....");
        }
    }
    });
}

I think there's no problem with 'deleting objects' since objects are never deleted, just removed from the list.

Imagine people are logging in and logging out at the same time.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top