.NET 中在单独(单个)线程上管理任务队列的最佳方式 [英] Best way in .NET to manage queue of tasks on a separate (single) thread

查看:32
本文介绍了.NET 中在单独(单个)线程上管理任务队列的最佳方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道这些年来异步编程发生了很多变化.我有点尴尬,因为我让自己在 34 岁就生疏了,但我指望 StackOverflow 使我跟上进度.

I know that asynchronous programming has seen a lot of changes over the years. I'm somewhat embarrassed that I let myself get this rusty at just 34 years old, but I'm counting on StackOverflow to bring me up to speed.

我想做的是在一个单独的线程上管理一个工作"队列,但以一次只处理一个项目的方式.我想在这个线程上发布工作,它不需要将任何东西传回给调用者.当然,我可以简单地启动一个新的 Thread 对象并让它在共享的 Queue 对象上循环,使用睡眠、中断、等待句柄等.但我知道事情有从那以后变得更好了.我们有 BlockingCollectionTaskasync/await,更不用说 NuGet 包,它们可能抽象了很多.

What I am trying to do is manage a queue of "work" on a separate thread, but in such a way that only one item is processed at a time. I want to post work on this thread and it doesn't need to pass anything back to the caller. Of course I could simply spin up a new Thread object and have it loop over a shared Queue object, using sleeps, interrupts, wait handles, etc. But I know things have gotten better since then. We have BlockingCollection, Task, async/await, not to mention NuGet packages that probably abstract a lot of that.

我知道什么是最好的..."问题通常不受欢迎,所以我将重新表述为当前推荐的是什么..."使用内置的 .NET 机制来完成这样的事情最好.但是,如果第三方 NuGet 包将事情简化了很多,那也无妨.

I know that "What's the best..." questions are generally frowned upon so I'll rephrase it by saying "What is the currently recommended..." way to accomplish something like this using built-in .NET mechanisms preferably. But if a third party NuGet package simplifies things a bunch, it's just as well.

我考虑了一个 TaskScheduler 实例,其最大并发数固定为 1,但现在似乎有一种不那么笨拙的方法来做到这一点.

I considered a TaskScheduler instance with a fixed maximum concurrency of 1, but seems there is probably a much less clunky way to do that by now.

背景

具体来说,在这种情况下,我想做的是在 Web 请求期间将 IP 地理定位任务排队.同一个 IP 可能会多次排队等待地理定位,但任务将知道如何检测并在已经解决的情况下尽早跳过.但是请求处理程序只会抛出这些 () =>LocateAddress(context.Request.UserHostAddress) 调用队列并让 LocateAddress 方法处理重复工作检测.我使用的地理定位 API 不喜欢被请求轰炸,这就是为什么我想一次将它限制为一个并发任务.但是,如果允许该方法通过简单的参数更改轻松扩展到更多并发任务,那就太好了.

Specifically, what I am trying to do in this case is queue an IP geolocation task during a web request. The same IP might wind up getting queued for geolocation multiple times, but the task will know how to detect that and skip out early if it's already been resolved. But the request handler is just going to throw these () => LocateAddress(context.Request.UserHostAddress) calls into a queue and let the LocateAddress method handle duplicate work detection. The geolocation API I am using doesn't like to be bombarded with requests which is why I want to limit it to a single concurrent task at a time. However, it would be nice if the approach was allowed to easily scale to more concurrent tasks with a simple parameter change.

推荐答案

要创建异步单度并行工作队列,您可以简单地创建一个 SemaphoreSlim,初始化为 1,然后让在开始请求的工作之前,排队方法 await 获取该信号量.

To create an asynchronous single degree of parallelism queue of work you can simply create a SemaphoreSlim, initialized to one, and then have the enqueing method await on the acquisition of that semaphore before starting the requested work.

public class TaskQueue
{
    private SemaphoreSlim semaphore;
    public TaskQueue()
    {
        semaphore = new SemaphoreSlim(1);
    }

    public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
    {
        await semaphore.WaitAsync();
        try
        {
            return await taskGenerator();
        }
        finally
        {
            semaphore.Release();
        }
    }
    public async Task Enqueue(Func<Task> taskGenerator)
    {
        await semaphore.WaitAsync();
        try
        {
            await taskGenerator();
        }
        finally
        {
            semaphore.Release();
        }
    }
}

当然,要获得固定的并行度,只需将信号量初始化为其他数字即可.

Of course, to have a fixed degree of parallelism other than one simply initialize the semaphore to some other number.

这篇关于.NET 中在单独(单个)线程上管理任务队列的最佳方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆