在Self Hosted ASP.NET Core Microservice中启动多个后台线程

Har*_*mar 1 c# multithreading .net-core kestrel-http-server asp.net-core

我在哪里可以创建多个长时间运行的后台线程,Self Hosted Self Contained ASP.NET Core Microservice其生命周期与微服务生命周期相同?因此,从线程检索的信息可以作为对请求的响应发送.

尝试给定代码,但它在后台线程忙时减少了http请求性能.Program.cs文件的主要方法是:

static void Main(string[] args)
{
    //Start background thread1
    //Start background thread2
    //Around 10 background threads
    //Start host
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseUrls(ServerUrl)
        .UseConfiguration(config)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .ConfigureServices(s => s.AddRouting())
        .Configure(app => app.UseRouter(r => { (new Router()).Route(r); }))
        .Build();
    host.Run();
}
Run Code Online (Sandbox Code Playgroud)

线程以这种方式工作:

Thread t1 = new Thread(StartWork);
t1.IsBackground = true;
t1.Start();

public void StartWork()
{
    while (ApplicationIsRunning)
    {
        //Get database info >> login into remote devices (SSH) >> get information >> process information >> update application variables and database
        Thread.Sleep(10000);
    }
}
Run Code Online (Sandbox Code Playgroud)

线程繁忙但CPU请求性能非常差时,CPU利用率仅为1-5%.睡觉后状态表现再次提高.

问题出在SSH客户端连接的连接方法上.在某些时候,connect方法没有响应,它也会影响所有其他线程.那很奇怪!

Renci.SshNet.SshClient sshClient = New Renci.SshNet.SshClient(sshConnectionInfo);
sshClient.Connect();
Run Code Online (Sandbox Code Playgroud)

如果一个线程由于任何原因而忙于连接,则不应影响其他线程.

Roe*_*t M 6

确定你可以:)使用IHostedService(从.net核心开箱即用),您可以实现以下功能:

public abstract class HostedService : IHostedService
    {

        private Task _executingTask;
        private CancellationTokenSource _cts;

        public Task StartAsync(CancellationToken cancellationToken)
        {
            // Create a linked token so we can trigger cancellation outside of this token's cancellation
            _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

            // Store the task we're executing
            _executingTask = ExecuteAsync(_cts.Token);

            // If the task is completed then return it, otherwise it's running
            return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            // Stop called without start
            if (_executingTask == null)
            {
                return;
            }

            // Signal cancellation to the executing method
            _cts.Cancel();

            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));

            // Throw if cancellation triggered
            cancellationToken.ThrowIfCancellationRequested();
        }

        // Derived classes should override this and execute a long running method until 
        // cancellation is requested
        protected abstract Task ExecuteAsync(CancellationToken cancellationToken);
    }


then you can implement your abstract class:


public class DataRefreshService : HostedService
{
    private readonly RandomStringProvider _randomStringProvider;

    public DataRefreshService(RandomStringProvider randomStringProvider)
    {
        _randomStringProvider = randomStringProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await _randomStringProvider.UpdateString(cancellationToken);
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的设置中,您只需添加依赖项:

services.AddSingleton<IHostedService, DataRefreshService>();
Run Code Online (Sandbox Code Playgroud)

RandomStringProvider只是一个例子.你得到的图片:)

.net核心为你自动连线,就像一个魅力!完美保持rabbitmq连接开放!

试一试!


Chr*_*att 5

尝试了给定的代码,但当后台线程忙时,它会降低 http 请求的性能。

首先要意识到的是,在 Web 应用程序的上下文中没有真正的后台工作。Web 服务器旨在快速为请求提供服务。有一个线程池,通常由多达 1000 个线程组成(通常称为服务器的“最大请求”,因为每个请求都需要一个线程)。该线程池是一种有限资源,当您将其最大化时,任何进一步的请求都会排队,直到线程再次可用。启动一个新线程需要从这个池中取出一个线程。因此,您的一个请求现在消耗了两个线程而不是一个线程。做这种类型的事情就足够了,你可以很容易地用少量的请求耗尽线程池,然后让你的 Web 服务器瘫痪。

至少,如果您正在执行某种异步工作,您可能会允许为请求提供服务的主线程在此新线程执行其操作时返回到池中,但即便如此,您也只是将一个线程换成另一个线程。然而,在这里,你甚至没有这样做。您的代码在此处处于阻塞状态,因此当您的线程池已经处于饥饿状态时,您现在已经让线程处于空闲状态。

长期和短期,不要这样做。如果有任何需要完成的工作需要花费相当多的时间,则该工作应该卸载到后台进程,即在您的 Web 应用程序上下文之外运行的可以在不影响您的 Web 性能的情况下完成工作的东西应用。您几乎不应该在 Web 应用程序中创建新线程。如果您发现自己这样做,则需要重新考虑您的设计。

  • “启动一个新线程需要从这个池中获取一个线程。” 这是不正确的。使用“new Thread”(如问题中所述)显式实例化线程根本不涉及线程池。即使这个线程来自池,只要后台线程的数量很小且恒定,而不是为每个请求创建一个(在问题中为两个),影响仍然可以忽略不计(就线程池饥饿而言) ,至少,资源是另一回事)。在每个请求上显式地创建一个(或两个)全新的线程确实是一个非常糟糕的主意。 (2认同)