C# asp.net内核中的队列任务

C# asp.net内核中的队列任务,c#,asp.net,asp.net-core,queue,semaphore,C#,Asp.net,Asp.net Core,Queue,Semaphore,例如,在功能方面,有20个用户,他们几乎一次点击了发送按钮,所以方法堆叠在队列中,发送第一条用户消息,接收第二条第三条之后的响应,依此类推。用户不会与其他人聊天,而是使用响应非常快的设备进行聊天 因此,我尝试将发送消息的任务排队。 我找到了使用任务队列的代码示例,如示例1和示例2所示。 示例1 public class SerialQueue { readonly object _locker = new object(); WeakReference<Task>

例如,在功能方面,有20个用户,他们几乎一次点击了发送按钮,所以方法堆叠在队列中,发送第一条用户消息,接收第二条第三条之后的响应,依此类推。用户不会与其他人聊天,而是使用响应非常快的设备进行聊天
因此,我尝试将发送消息的任务排队。
我找到了使用任务队列的代码示例,如示例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;
        }
    }
}
公共类串行队列
{
只读对象_locker=新对象();
WeakReference\u lastTask;
公共任务排队(操作)
{
返回排队(()=>{
动作();
返回null;
});
}
公共任务排队(Func函数)
{
锁(储物柜)
{
Task lastTask=null;
任务结果任务=null;
if(_lastTask!=null&&u lastTask.TryGetTarget(out lastTask))
{
resultTask=lastTask.ContinueWith(=>function());
}
其他的
{
resultTask=Task.Run(函数);
}
_lastTask=新的WeakReference(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();
        }
    }
}
公共类任务队列
{
私有只读信号量limu信号量lim;
公共任务队列()
{
_semaphoreSlim=新的semaphoreSlim(1);
}
公共异步任务排队(Func taskGenerator)
{
wait_semaphoreSlim.WaitAsync();
尝试
{
返回等待任务生成器();
}
最后
{
_semaphoreSlim.Release();
}
}
公共异步任务排队(Func taskGenerator)
{
wait_semaphoreSlim.WaitAsync();
尝试
{
等待任务生成器();
}
最后
{
_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();
        }
[HttpPost(Name=“添加消息”)]
公共异步任务PostMessage([FromBody]Messenger视图模型Messenger视图模型)
{
TaskQueue TaskQueue=新建TaskQueue();
SerialQueue SerialQueue=新的SerialQueue();
等待taskQueue.Enqueue(()=>SendMessage(Messenger ViewModel.PhoneNr、Messenger ViewModel.MessageBody、,
messengerViewModel.ContactId,messengerViewModel.State));
//我不会同时运行任务,一次使用一个或另一个
等待serialQueue.Enqueue(()=>SendMessage(Messenger ViewModel.PhoneNr、Messenger ViewModel.MessageBody、,
messengerViewModel.ContactId,messengerViewModel.State));
返回Ok();
}

如何解决问题并通过每次单击将任务堆叠到队列中?

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

您应该使用静态/单例队列,以便每次单击/调用都在同一队列对象上工作

但当你在多台服务器上扩展你的webapp时,这会带来问题。为此,您应该(例如)将Azure队列存储与Azure函数结合使用


Startup.cs
public void配置服务(IServiceCollection服务)
{
services.AddSingleton();
services.AddSingleton();
//其余的
}
SomeController.cs
[HttpPost(Name=“添加消息”)]
公共异步任务后消息(
[FromBody]Messenger视图模型Messenger视图模型,
[FromServices]任务队列任务队列,
[FromServices]串行队列(串行队列)
{
等待任务队列。排队(
()=>发送消息(
Messenger ViewModel.PhoneNr,
Messenger ViewModel.MessageBody,
Messenger ViewModel.ContactId,
信使视图模型(State));
//我不会同时运行任务,一次使用一个或另一个
等待串行队列。排队(
()=>发送消息(
Messenger ViewModel.PhoneNr,
Messenger ViewModel.MessageBody,
Messenger ViewModel.ContactId,
信使视图模型(State));
返回Ok();
}

感谢您的回复,此程序将托管在本地服务器上,因此我不知道连接多台服务器是否会出现问题。如果我理解正确,我需要将队列作为singleton注入Startup.cs?正确,然后使用DI获取队列。这样,您总是使用相同的对象
services.AddSingleton()以这种方式注入队列,但仍然发生相同的情况,或者我遗漏了一些重要的内容?添加了如何在控制器中使用它的示例
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<TaskQueue>();
    services.AddSingleton<SerialQueue>();
    // the rest
}
[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();
}