C# ASP.NET核心应用程序中每个用户一个长时间运行的进程
我有一个异步函数,它将执行一些长时间运行和CPU密集型任务。因为它是异步的,所以不会占用UI 在正常情况下,我对它的运行方式没有问题。该进程在单击按钮时触发,但也可以在API调用时触发 我发现,如果我连续多次快速点击按钮,整个网站就会开始反应缓慢。这是一种滥用,但对性能有不利影响 我想在内存中实现一个用户调用函数的列表,这样每个用户都可以有一个实例来运行长时间运行的函数,但它不会接受来自该用户的任何进一步请求C# ASP.NET核心应用程序中每个用户一个长时间运行的进程,c#,asp.net-core,task,long-running-processes,C#,Asp.net Core,Task,Long Running Processes,我有一个异步函数,它将执行一些长时间运行和CPU密集型任务。因为它是异步的,所以不会占用UI 在正常情况下,我对它的运行方式没有问题。该进程在单击按钮时触发,但也可以在API调用时触发 我发现,如果我连续多次快速点击按钮,整个网站就会开始反应缓慢。这是一种滥用,但对性能有不利影响 我想在内存中实现一个用户调用函数的列表,这样每个用户都可以有一个实例来运行长时间运行的函数,但它不会接受来自该用户的任何进一步请求 谁知道有什么聪明的方法来实现这一点,或者以前做过类似的事情。我正在考虑一种类似thre
谁知道有什么聪明的方法来实现这一点,或者以前做过类似的事情。我正在考虑一种类似threadsafe的集合,您可以将任务添加到其中,但如果您尝试添加。添加一个已经为某个用户/claimsprincipal运行的任务,那么它将无法添加该任务。您的解决方案存在一些问题:
异步
,它仍然可以保留UI。一旦所有CPU都忙了,您将看到性能影响在web和应用服务器之间使用最适合您需要的通信形式:WCF、REST API或消息队列框架,如MSMQ或RabbitMQ。您的解决方案存在一些问题:
异步
,它仍然可以保留UI。一旦所有CPU都忙了,您将看到性能影响在web和应用服务器之间使用最适合您需要的通信形式:WCF、REST API或消息队列框架,如MSMQ或RabbitMQ。不必深入探讨这样做是否适合您的特定业务情况和体系结构,我认为您的问题基本上可以归结为:如何确保用户一次只能调用一个特定流程 您可以使用threadsafe集合为每个用户保留一个标志,指示他们当前是否正在运行这样的进程。在异步代码中使用try{}finally{}块来管理值。在try块中将其设置为true,在finally块中将其设置为false。在将其设置为true之后,在最终执行之前,您将完成长时间运行的其他工作。概述如下:
try{
//if thread safe value for this user is already true then exit.
//set thread safe collection value for this user to true
//Do work
}finally{
//set thread safe collection value for this user to false;
}
如果是我,我会在集合中放置一个对象,其中该对象有一个值,即bool。这样,当我检查对象的值或设置它的值时,我可以使用lock
关键字为该用户锁定该对象,这样我就可以确保在我检查它和设置它之间没有其他进程更改该值
希望这能有所帮助。在不深入讨论这样做是否适合您的特定业务情况和体系结构的问题的情况下,我认为您的问题基本上可以归结为:如何确保用户一次只能调用一个特定流程 您可以使用threadsafe集合为每个用户保留一个标志,指示他们当前是否正在运行这样的进程。在异步代码中使用try{}finally{}块来管理值。在try块中将其设置为true,在finally块中将其设置为false。在将其设置为true之后,在最终执行之前,您将完成长时间运行的其他工作。概述如下:
try{
//if thread safe value for this user is already true then exit.
//set thread safe collection value for this user to true
//Do work
}finally{
//set thread safe collection value for this user to false;
}
如果是我,我会在集合中放置一个对象,其中该对象有一个值,即bool。这样,当我检查对象的值或设置它的值时,我可以使用lock
关键字为该用户锁定该对象,这样我就可以确保在我检查它和设置它之间没有其他进程更改该值
希望这会有所帮助。最后,我实现了一个服务,作为我在DI注册的一个单例
public class SingleInstanceRegisterService: IDisposable
{
private ConcurrentDictionary<SingleInstance,SingleInstance> dict = new ConcurrentDictionary<SingleInstance, SingleInstance>();
AutoResetEvent arv;
Timer timer;
public SingleInstanceRegisterService()
{
arv = new AutoResetEvent(false);
timer = new Timer(this.Cleanup, arv, 0, 60000);
}
public bool TryAdd(SingleInstance i)
{
return dict.TryAdd(i, i);
}
public bool ContainsKey(SingleInstance i)
{
return dict.ContainsKey(i);
}
public bool TryRemove(SingleInstance i)
{
SingleInstance x;
return dict.TryRemove(i, out x);
}
public void Cleanup(Object stateInfo)
{
AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
if (dict != null)
{
foreach(SingleInstance i in dict.Keys)
{
if (i.Expiry < DateTimeOffset.Now)
{
TryRemove(i);
}
}
}
autoEvent.Set();
}
public void Dispose()
{
timer?.Dispose();
timer = null;
}
}
如果TryAdd添加失败,则它必须已经在运行。请注意,有一个计时器将自动清除已过期的进程锁 最后,我实现了一个服务,作为我向DI注册的单例
public class SingleInstanceRegisterService: IDisposable
{
private ConcurrentDictionary<SingleInstance,SingleInstance> dict = new ConcurrentDictionary<SingleInstance, SingleInstance>();
AutoResetEvent arv;
Timer timer;
public SingleInstanceRegisterService()
{
arv = new AutoResetEvent(false);
timer = new Timer(this.Cleanup, arv, 0, 60000);
}
public bool TryAdd(SingleInstance i)
{
return dict.TryAdd(i, i);
}
public bool ContainsKey(SingleInstance i)
{
return dict.ContainsKey(i);
}
public bool TryRemove(SingleInstance i)
{
SingleInstance x;
return dict.TryRemove(i, out x);
}
public void Cleanup(Object stateInfo)
{
AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
if (dict != null)
{
foreach(SingleInstance i in dict.Keys)
{
if (i.Expiry < DateTimeOffset.Now)
{
TryRemove(i);
}
}
}
autoEvent.Set();
}
public void Dispose()
{
timer?.Dispose();
timer = null;
}
}
如果TryAdd添加失败,则它必须已经在运行。请注意,有一个计时器将自动清除已过期的进程锁 阅读本文,了解为什么在ASP.NET中运行CPU绑定的异步任务是一个可怕的想法(也适用于ASP.NET核心)!仅将异步用于真正的异步操作,如I/O(文件、网络)或数据库连接。从不用于CPU限制的任务。在后台线程或任务中执行CPU密集型任务只对UI开发(WPF、Windows Phone applic)有用