在.NET最好的方法来管理一个单独的(单一)线程的任务队列最好的、队列、线程、方法来

2023-09-03 17:03:23 作者:么么哒

我知道,异步编程已经看到了很多多年来的变化。我有点不好意思,我让自己得到这个生锈的只有34岁,但我指望计算器给我加快速度。

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.

我所要做的是管理上的一个单独的线程工作的队列中,但是在只有一个项目在一个时间处理的这样一种方式。我要张贴工作在此线程,它并不需要传递任何回给调用者。当然,我可以简单地旋转了一个新的对象,并使其循环通过共享队列对象,用睡觉,中断,等待句柄,等等。但我知道事情变得从那时起更好。我们有 BlockingCollection 工作异步 / 等待,更不用提的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机制preferably。但是,如果一个第三方的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.

我认为为1的固定最大并发一个的TaskScheduler 实例,但似乎有可能是一个更笨重的方式来做到这一点了。

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 ,初始化工作并行队列的异步单一度一个,然后有enqueing方法等待就收购该信号的启动被请求的工作之前。

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.