0

I want to optimize My server side speed and performance to manage clients, I think I have two way to mange them:

1.Create manually one thread per client connection

2.create SynchronizationContext per client (this will manage threads on background)

Suppose We have one million users connected:

The first way is faster but I don't know is optimize and better way to manage send/receive data from client or not?

What is your suggestion to make better performance and speed to manage all of clients without lags and hangs?

My example to test SynchronizationContext and multi thread on console:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SynchronizationContextForMethod
{

    public class ServiceClass
    {
        public void Login(string name)
        {
            OperationContext<ClientInfo>.Current = new ClientInfo() { Name = name };
        }

        public string WhatIsMyName()
        {
            return OperationContext<ClientInfo>.Current.Name;
        }
    }

    public class ClientInfo
    {
        public string Name { get; set; }
        public string UserName { get; set; }
    }

    public class OperationContext<T>
    {
        static ConcurrentDictionary<SynchronizationContext, T> ContextByThread = new ConcurrentDictionary<SynchronizationContext, T>();
        public static T Current
        {
            get
            {
                ContextByThread.TryGetValue(SynchronizationContext.Current, out T value);
                return value;
            }
            set
            {
                ContextByThread.TryAdd(SynchronizationContext.Current, value);
            }
        }

        public static void EncContext()
        {
            ContextByThread.TryRemove(SynchronizationContext.Current, out T value);
        }
    }

    class Program
    {
        static List<SynchronizationContext> _contexts = new List<SynchronizationContext>();
        static void Main(string[] args)
        {
            ThreadPool.GetMaxThreads(out int worker, out int ports);

            ThreadPool.SetMaxThreads(int.MaxValue, int.MaxValue);
            Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);

            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            _contexts.Add(SynchronizationContext.Current);
            var service = new ServiceClass();

            for (int i = 0; i < 20; i++)
            {
                //PostWithNewThread((state) =>
                PostNormally((state) =>
                {
                    GC.Collect();
                    if (SynchronizationContext.Current == null)
                    {
                        SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
                    }
                    else
                    {
                        //no run ever
                    }
                    Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);
                    var name = state.ToString();
                    service.Login(name);
                    var isTrue = name == service.WhatIsMyName();
                    if (!isTrue)
                    {
                        //if false this is wrong code!
                    }
                    Console.WriteLine($"service login {name}: " + isTrue);
                    Thread.Sleep(5000);
                    Console.WriteLine($"service " + name + " finished");
                    OperationContext<ClientInfo>.EncContext();
                }, "ali" + i);
                Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);

                //PostWithNewThread((state) =>
                PostNormally((state) =>
                {
                    GC.Collect();
                    if (SynchronizationContext.Current == null)
                    {
                        SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
                    }
                    else
                    {
                        //no run ever
                    }
                    Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);
                    var name = state.ToString();
                    service.Login(name);
                    var isTrue = name == service.WhatIsMyName();
                    if (!isTrue)
                    {
                        //if false this is wrong code!
                    }
                    Console.WriteLine($"service login {name}: " + isTrue);
                    Console.WriteLine($"service " + name + " finished");
                    OperationContext<ClientInfo>.EncContext();
                }, "reza" + i);
                Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);

                //PostWithNewThread((state) =>
                PostNormally((state) =>
                {
                    GC.Collect();
                    if (SynchronizationContext.Current == null)
                    {
                        SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
                    }
                    else
                    {
                        //no run ever
                    }
                    Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);
                    Thread.Sleep(2000);
                    var name = state.ToString();
                    service.Login(name);
                    var isTrue = name == service.WhatIsMyName();
                    if (!isTrue)
                    {
                        //if false this is wrong code!
                    }
                    Console.WriteLine($"service login {name}: " + (isTrue));
                    Console.WriteLine($"service " + name + " finished");
                    OperationContext<ClientInfo>.EncContext();
                }, "hassan" + i);
            }
            Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);
            while (true)
            {
                GC.Collect();
                Thread.Sleep(1000);
                Console.WriteLine("thread count : " + Process.GetCurrentProcess().Threads.Count);
            }
        }

        public static void PostNormally(SendOrPostCallback run, object state)
        {
            SynchronizationContext.Current.Post(run, state);
        }

        public static void PostWithNewThread(SendOrPostCallback run, object state)
        {
            Thread thread = new Thread(() =>
            {
                run(state);
            });
            thread.IsBackground = true;
            thread.Start();
        }
    }
}
Ali Yousefi
  • 2,355
  • 2
  • 32
  • 47
  • 3
    My suggestion would be to use existing web server technology (asp.net and so on) and not roll your own, if you want to handle millions of clients. – Evk May 24 '18 at 10:45
  • Neither? Thread-per-client is known to not scale to vast numbers and Synchronization-context-per-client looks like it'll effectively by trying to do the same sort of thing just hiding some of the details. Why do you think you need to dedicate any *specific* resource to each client beyond a simple data object containing their socket or other connection-specific resource? – Damien_The_Unbeliever May 24 '18 at 10:46
  • @Evk I don't want to use iis or xamp etc, I want to know how they are working. – Ali Yousefi May 24 '18 at 11:02
  • @Damien_The_Unbeliever in my test multi thread will make 50 thread and Synchronizationcontext will make 20 thread,i logged threads of process – Ali Yousefi May 24 '18 at 11:05
  • Agree with @Evk. Use existing stacks - those will have already solved masses of problems which you haven't thought of, will comply with various standards, will hook easily into various other things, and will simply let you worry about what your app actually *does*. Look into .Net Core and/or OWIN self-hosted apps. You can easily avoid IIS but still have nice things. Oh, and make everything Async. That's designed specifically to deal with this sort of concern – GPW May 24 '18 at 12:03
  • If you are doing this for learning - the best way is to make request processing asynchronous. There is a lot of IO involved in request processing, and during that IO there is no need to use any threads. Then, you can handle a lot of requests with small amount of threads, because they do not waste time blocked waiting for IO to complete. – Evk May 24 '18 at 12:19

0 Answers0