ASP.NET Core中的排队任务

时间:2018-06-20 08:51:09

标签: c# asp.net asp.net-core queue semaphore

例如功能的实现有20个用户,他们几乎一次单击了发送按钮,因此在队列中堆叠的方法和发送第一个用户消息并接收响应的方法,在第二个,第三个之后等等。用户不会与其他人聊天,但会使用响应速度非常快的设备
所以我试图将发送消息的Task排队。
我发现了使用任务排队的代码示例,如示例1 示例2 所示。

示例1

public class SerialQueue
{
    readonly object _locker = new object();
    WeakReference<Task> _lastTask;

    public Task Enqueue(Action action)
    {
        return Enqueue<object>(() => {
            action();
            return null;
        });
    }

    public Task<T> Enqueue<T>(Func<T> function)
    {
        lock (_locker)
        {
            Task lastTask = null;
            Task<T> resultTask = null;

            if (_lastTask != null && _lastTask.TryGetTarget(out lastTask))
            {
                resultTask = lastTask.ContinueWith(_ => function());
            }
            else
            {
                resultTask = Task.Run(function);
            }

            _lastTask = new WeakReference<Task>(resultTask);
            return resultTask;
        }
    }
}


示例2

 public class TaskQueue
{
    private readonly SemaphoreSlim _semaphoreSlim;

    public TaskQueue()
    {
        _semaphoreSlim = new SemaphoreSlim(1);
    }

    public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
    {
        await _semaphoreSlim.WaitAsync();

        try
        {
            return await taskGenerator();
        }
        finally
        {
            _semaphoreSlim.Release();
        }
    }

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


问题是,当我每次按按钮传递要排队的任务(示例3 )时,任务仍然同时执行并且彼此中断。

示例3

 [HttpPost(Name = "add-message")]
        public async Task<IActionResult> PostMessage([FromBody] MessengerViewModel messengerViewModel)
        {
            TaskQueue taskQueue = new TaskQueue();
            SerialQueue serialQueue = new SerialQueue();

            await taskQueue.Enqueue(() => SendMessage(messengerViewModel.PhoneNr, messengerViewModel.MessageBody,
                messengerViewModel.ContactId, messengerViewModel.State));
//I'm not running tasks at same time, using one or other at time
            await serialQueue.Enqueue(() => SendMessage(messengerViewModel.PhoneNr, messengerViewModel.MessageBody,
                messengerViewModel.ContactId, messengerViewModel.State));

            return Ok();
        }

我该如何解决问题并将堆栈任务按每次单击排队?

1 个答案:

答案 0 :(得分:3)

您的问题是您每次都创建一个新的TaskQueueSerialQueue。因此,每次用户单击/调用PostMessage时都会创建一个新队列,该任务是队列中的第一个任务并直接执行。

您应该使用静态/单个队列,以便每次单击/调用都可在同一队列对象上进行。

但是,当您跨多个服务器扩展Web应用程序时,这会带来问题。为此,您应该结合使用Azure Queue Storage之类的东西(例如)与Azure Functions相结合。


Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<TaskQueue>();
    services.AddSingleton<SerialQueue>();
    // the rest
}

SomeController.cs

[HttpPost(Name = "add-message")]
public async Task<IActionResult> PostMessage(
    [FromBody] MessengerViewModel messengerViewModel,
    [FromServices] TaskQueue taskQueue,
    [FromServices] SerialQueue serialQueue)
{
    await taskQueue.Enqueue(
        () => SendMessage(
                  messengerViewModel.PhoneNr, 
                  messengerViewModel.MessageBody,
                  messengerViewModel.ContactId, 
                  messengerViewModel.State));
    //I'm not running tasks at same time, using one or other at time
    await serialQueue.Enqueue(
        () => SendMessage(
                  messengerViewModel.PhoneNr, 
                  messengerViewModel.MessageBody,
                  messengerViewModel.ContactId, 
                  messengerViewModel.State));

    return Ok();
}