英文:
Limit Running Threads to one in ASP.NET Core 6 using TaskScheduler
问题
我们有一个ASP.NET Core 6应用程序。用户上传文件,然后我们对这些文件进行处理。
处理文件是一个消耗CPU和内存的操作。我们计划将这些操作移到我们的主应用程序之外,但在这之前,我们决定尝试在上传文件后在后台运行任务,并在操作完成后发送电子邮件。
因此,文件的处理过程如下:
public async Task ProcessFile(string filePath, ...)
{
var stage1 = await ProcessStage1(...);
var stage2 = await ProcessStage2(...);
await SendEmail(..);
}
从上传文件开始,我们运行任务:
public async Task UploadFile(Stream stream....)
{
// .... 上传文件
Task.Factory.StartNew(() => {
ProcessFile(....);
})
}
由于这个过程很重,所以它会消耗大量内存,因此我们决定通过限制线程数为两个或一个来将处理限制为一次处理一个文件。
阅读 .NET TaskScheduler 限制线程数为一的示例 中的示例后,我实现了示例中提到的代码,并将自定义的 TaskScheduler
添加为单例,如下所示:
public interface IProcessFileFactory {
public TaskScheduler Scheduler { get; }
public TaskFactory Factory { get; }
}
public ProcessFileFactory: IProcessFileFactory
{
public TaskScheduler Scheduler { get; }
public TaskFactory Factory { get; }
public ProcessFileFactory()
{
Scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
Factory = new TaskFactory(Scheduler);
}
// 然后将其添加为DI中的单例
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(TaskScheduler, LimitedConcurrencyLevelTaskScheduler)
}
// 然后使用它
IProcessFileFactory.Factory.StartNew(() => {
ProcessFile(...),
..., IProcessFileFactory.Scheduler);
}
然后我尝试了不同的方式:
Services.AddSingleton<TaskScheduler>(x => new LimitedConcurrencyLevelTaskScheduler(2));
// 然后使用它
Task.Factory.StartNew(() => {
ProcessFile()
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler);
这两种情况都没有奏效,它仍然处理超过2个文件。
英文:
We have an ASP.NET Core 6 application. The user upload files and we do process those files.
Processing the files is a CPU and memory extensive operation. We are planning to take these operations outside of our main application, but until we do that we decided to try to run the task in background after uploading the file, and send email when the operation is done.
So the process of file is:
public async Task ProcessFile(string filePath, ...)
{
var stage1 = await ProcessStage1(...);
var stage2 = await ProcessStage2(...);
await SendEmail(..);
}
From the upload the file we run the task.run
public async Task UploadFile(Stream stream....)
{
// .... uploading the file
Task.Factory.StartNew(() => {
ProcessFile(....);
})
}
Because the process is heavy, so it is consuming lots of memory, so we decided to limit the process to one file at one time by limiting the threads to only two or one.
Reading the example in .NET TaskScheduler to limit the threads to one , I implemented the code mentioned in the example, and I added the custom TaskScheduler
as singleton as the following:
public interface IProcessFileFactory {
public TaskScheduler Scheduler { get; }
public TaskFactory Factory { get; }
}
public ProcessFileFactory: IProcessFileFactory
{
public TaskScheduler Scheduler { get; }
public TaskFactory Factory { get; }
public ProcessFileFactory()
{
Scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
Factory = new TaskFactory(Scheduler);
}
// then add it as singlton in DI
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(TaskScheduler, LimitedConcurrencyLevelTaskScheduler)
}
/// and then use it
IProcessFileFactory.Factory.StartNew(() => {
ProcessFile(...),
..., IProcessFileFactory.Scheduler);
And I tried it in a different way
Services.AddSignlton<TaskScheduler>(x => new LimitedConcurrencyLevelTaskScheduler (2) );
// and then use it
Task.Factory.StartNew(() {
ProcessFile()
},
CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler);
Both cases didn't work, and it is processing files for more than 2.
答案1
得分: 1
That is the correct solution, as I describe on my blog.
TaskScheduler
is a low-level primitive that isn't usually used with async
code. It sounds like you just need a queue service. As long as you process the work items one at a time, nothing more complex is necessary.
As a reminder, in-memory queues such as channels are not durable and thus you can lose work on any application shutdown. A proper solution uses a durable queue, and (preferably) a separate worker process outside your main application to perform the operations.
英文:
> We are planning to take these operations outside of our main application...
That is the correct solution, as I describe on my blog.
> ... TaskScheduler ...
TaskScheduler
is a low-level primitive that isn't usually used with async
code. It sounds like you just need a queue service. As long as you process the work items one at a time, nothing more complex is necessary.
As a reminder, in-memory queues such as channels are not durable and thus you can lose work on any application shutdown. A proper solution uses a durable queue, and (preferably) a separate worker process outside your main application to perform the operations.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论