1

I'm trying build a worker service on Core 5.0. My tree is basically like that =>
1 -) Program.cs 2-) Worker.cs 3-) MyStartUp.cs 4-) Client.cs

In MyStartUp.cs I am getting a list and calling Client class some servers according to list.
In the Client class, I connect to the devices and write the data I read to the database.
Device count nearly 1200, server way is TCP/IP.
What is your best suggestion for write a worker service like that? How can I use threads in it best form?

Below is my first try. This form is working but it's so slow for 1000 different client because there is so much reads in client.

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        StartUp startUp = new StartUp();
    }
}

public class StartUp
{
    public StartUp()
    {
        //... get client datas and initialize client object
        StartClients();
    }
    public void StartClients()
    {
        foreach (var item in ClientList)
        {
            new Thread(item.Run).Start();
        }
    }
}

public class Client
{
    System.Timers.Timer timer ;
    public Client()
    {
        timer = new Timer();
        timer.Interval = 100;
        timer.Elapsed += Timer_Elapsed;
        
        //... initialize client connection and database
    }
    public void Run()
    {
        timer.Start();
    }
    private void Timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        //... write values of read client to database
    }
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
rehdadth
  • 66
  • 1
  • 7
  • Do I understand correctly that you are basically asking for the most efficient way to connect to and read data from 1000+ network clients? – Good Night Nerd Pride May 23 '22 at 14:54
  • Exactly like that – rehdadth May 23 '22 at 14:59
  • You might find some good ideas here: [Run async method regularly with specified interval](https://stackoverflow.com/questions/30462079/run-async-method-regularly-with-specified-interval). – Theodor Zoulias May 23 '22 at 16:15
  • One tip: do not try to parallelize storing the processed data in the database. You should collect and process the data in parallel into a list or something similar and then do one big bulk insert of all the data into the database. – Good Night Nerd Pride May 24 '22 at 07:47

1 Answers1

1

Say that you have 1k timers that run every 100ms, and say that each timer tick takes 50ms to execute. That means each timer needs 500ms/s, or 50% of one core, and you would need 500 cores to keep up with the work. You probably do not have that many cores, nor IO to process the requests, and that means the work will start piling up and your computer will more or less freeze since it does not have time to update the UI.

50ms might be an overestimation of the time used, but even at 5ms you would probably have issues unless you are running this on a monster server.

The solution would be to decrease the polling frequency to something more reasonable, say every 100s instead of every 100ms. Or to have one or more threads that polls your devices as fast as they can. For example something like:

private BlockingCollection<MyClient> clients = new ();
private List<Task> workers = new ();
public void StartWorker(){
    workers.Add(Task.Run(Run));
    void Run(){
        foreach(var client in clients.GetConsumingEnumerable()){
           // Do processing
           clients.Add(client); // re-add client to queue
      }
   } 
}
public void CloseAllWorkers(){
    clients.CompleteAdding();
    Task.WhenAll(workers).Wait();
}

I would note that usages of Thread is mostly superseded by tasks. And that creating a thread just to start a System.Timers.Timer is completely useless since the timer will run the tick event on the threadpool, regardless of the thread that started it. At least unless a synchronization object was specified.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • I understood what you said, but there are some applications that do this very easily. That's why I thought it could be done, for example a program with 700 devices read consumes about 10% cpu and about 750 mb ram – rehdadth May 23 '22 at 15:13
  • to be honest , iam trying connect and read some modbus tcp ip servers , siemens, abb, wago or somethink like that. Tcp answer packages is very small but so much – rehdadth May 23 '22 at 15:16
  • @rehdadth it will depend on the what you are doing in the processing. If "reading a device" means checking a memory value updated by DMA, then you can absolutely have a very high device count and update frequency. But if you are talking about things like network and/or database requests you are probably talking milliseconds. But there is no reason to guess! Just measure the time to read one device using a stopwatch, that will tell you how long time it takes, and would let you calculate how frequently you can expect to do a update. – JonasH May 23 '22 at 15:20
  • It is also perfectly possible you can optimize the requests, but that is not shown in your example, so there is no way to tell how efficient it is. In general you should start any optimization by profiling so you know where the actual problem is, and if you have made an improvement. – JonasH May 23 '22 at 15:23
  • then I will make the question cleaner and ask it again under this title, but I need to learn clearly; What is the difference between system.threading.timer and system.timer.timer? Which one do you think is more suitable for the building we are talking about? – rehdadth May 23 '22 at 15:41
  • @rehdadth there is not much difference between threading.Timer & timer.TImer. The later is a wrapper around the former, and supports optional synchronization. – JonasH May 23 '22 at 19:44
  • Thanks a lot for your information. @JonasH. İ read an article about thread and timers. Now I understand better what you said. I'll try to build the structure in my head. If there is a problem, I will ask for your help again. – rehdadth May 23 '22 at 20:46