.NET核心中的多线程工人服务

发布于 2025-01-31 14:45:58 字数 1390 浏览 5 评论 0原文

我正在尝试在Core 5.0上建立工人服务。我的树基本上像那样=>
1-)program.cs 2-)worker.cs 3-)mystartup.cs 4-)client.cs

in mystartup.cs中我正在获取列表并根据列表调用客户端一些服务器。

在客户端类中,我连接到设备并将读取的数据写入数据库。
设备计数将近1200,服务器方式是TCP/IP。
您对这样的工人服务的最佳建议是什么? 如何使用最佳形式的线程?

以下是我的第一次尝试。此表格工作起作用,但是对于1000个不同的客户端来说,它是如此慢,因为客户端的读取太多了。

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
    }
}

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
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

挽清梦 2025-02-07 14:45:59

假设您有每100ms运行的1K计时器,并说每个计时器滴答作用需要50毫秒才能执行。这意味着每个计时器都需要500ms/s或一个核心的50%,您需要500个内核才能跟上工作。您可能没有太多的内核,也没有IO处理请求,这意味着工作将开始堆积,并且您的计算机或多或少会冻结,因为它没有时间更新UI。

50毫秒可能是对使用时间的高估,但是即使在5ms时,您也可能会遇到问题,除非您在Monster服务器上运行它。

解决方案是将轮询频率降低到更合理的事物,例如每100毫秒而不是每100ms。或拥有一个或多个线程,可以尽可能快地对设备进行轮询。例如:

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();
}

我会注意到thread的用法主要由任务取代。而创建线程只是为了启动system.timers.timer是完全没有用的,因为计时器将在线程池上运行tick事件,而不管启动的线程如何。至少除非指定同步对象。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文