问题
Where can i create multiple long running background threads in Self Hosted Self Contained ASP.NET Core Microservice
whose lifetime is same as micro-service lifetime? So that information retrieved from threads can be sent as a response to the requests.
Tried given code but it reduces http request performance when background threads are busy. Main method of Program.cs file is:
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();
}
Threads work in this way:
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 utilization is only 1-5% when threads are busy but still http request performance is very bad. After going to sleep state performance again improves.
The issue is at connect method of SSH client connection. At some point connect method is not responding and it affect all other threads also. That is strange!
Renci.SshNet.SshClient sshClient = New Renci.SshNet.SshClient(sshConnectionInfo);
sshClient.Connect();
If one thread is busy in connection because of any reason it should not affect other threads.
回答1:
EDIT: code possibly from Steve Gordon's post at https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice ?
sure you can :) With IHostedService (out-of-the-box from .net core) you can implement the following:
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);
}
}
}
and in your setup you only have to add the dependency:
services.AddSingleton<IHostedService, DataRefreshService>();
RandomStringProvider
is just an example. You get the picture :)
.net core wires this automatically for you, works like a charm! Perfect to keep a rabbitmq-connection open!
give it a shot!
回答2:
Tried given code but it reduces http request performance when background threads are busy.
The first thing to realize is that there is no true background work in the context of a web application. Web servers are designed to quickly service requests. There's a thread pool, usually composed of up to 1000 threads (often referred to as the "max requests" of the server, due to each request requiring a thread). That thread pool is a finite resource, and when you max it out, any further requests are queued until a thread becomes available again. Spinning up a new thread takes a thread from this pool. As a result, your one request is now consuming two threads instead of one. Do this type of stuff enough, and you can easily exhaust the thread pool with merely a handful of requests, which then brings your web server to its knees.
At least if you were doing some sort of async work, you might allow the main thread servicing the request to be returned to the pool while this new thread does its thing, but even then, you're simply trading one thread for another. However, here, you're not even doing that. Your code here is blocking, so you've now got threads sitting idle, when your thread pool is already being starved.
Long and short, don't do this. If there's any work that needs to be done that takes a not insignificant amount of time, that work should be offloaded to a background process, i.e. something running outside the context of your web application that can do the work without affecting the performance of your web application. You should pretty much never create a new thread in a web application. If you find yourself doing so, you need to rethink your design.
来源:https://stackoverflow.com/questions/47035503/start-multiple-background-threads-inside-self-hosted-asp-net-core-microservice