一尘不染

.NET中在单独(单个)线程上管理任务队列的最佳方法

c#

我知道异步编程多年来已经发生了很多变化。令我感到困惑的是,我在34岁的时候就让自己生锈了,但是我指望着StackOverflow可以使我快速入门。

我想做的是在一个单独的线程上管理“工作”队列,但是每次只能处理一个项目。我想在此线程上发布工作,它不需要将任何内容传递回调用方。当然,我可以简单地启动一个新Thread对象,并Queue使用睡眠,中断,等待句柄等使其在共享对象上循环。但是我知道从那以后情况已经变得更好。我们有BlockingCollectionTaskasync/
await,更不用提的NuGet包,可能抽象了很多的。

我知道,“什么是最好的…”问题通常会被忽略,因此我将通过说“最好使用什么?”的方式来改写它,最好使用内置的.NET机制来完成类似的工作。但是,如果第三方NuGet程序包简化了一堆事情,那也是一样。

我考虑了一个TaskScheduler实例,其最大并发固定为1,但是到目前为止,似乎没有那么笨拙的方法了。

背景

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


阅读 349

收藏
2020-05-19

共1个答案

一尘不染

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

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

当然,除了具有固定的并行度之外,只需将信号量初始化为其他数字即可。

2020-05-19