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

时间:2017-10-31 12:31:52

标签: c# multithreading asp.net-core .net-core kestrel-http-server

我在哪里可以在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();
}

线程以这种方式工作:

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

线程繁忙但CPU请求性能非常差时,CPU利用率仅为1-5%。进入睡眠状态再次改善状态。

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

Renci.SshNet.SshClient sshClient = New Renci.SshNet.SshClient(sshConnectionInfo);
sshClient.Connect();

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

2 个答案:

答案 0 :(得分: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);
        }
    }
}

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

services.AddSingleton<IHostedService, DataRefreshService>();

RandomStringProvider只是一个例子。你得到的照片:))

.net核心为您自动连接,就像一个魅力!完美地保持兔子连接开放!

试一试!

答案 1 :(得分:2)

  

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

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

至少如果你正在进行某种异步工作,你可能会允许服务请求的主线程返回到池中,而这个新线程可以做到这一点,但即便如此,你只需要交换一个线程另一个。但是,在这里,你甚至没有这样做。这里的代码是阻塞的,所以当你的线程池已经被饿死时,你现在让线程处于空闲状态。

长短不要这样做。如果需要完成任何需要花费不少时间的工作,那么应该将该工作卸载到后台进程,即在Web应用程序的上下文之外运行的可以执行工作的内容而不会影响您的Web应用程序的性能。您几乎不应该在Web应用程序中创建新线程。如果您发现自己这样做,则需要重新考虑您的设计。