在我的Web应用程序中,我创建了一个方法,根据传递的URL创建GET请求,然后使用HttpClient创建GET调用。问题是一些GET调用可能需要很长时间才能完成,当该请求正在处理时,用户可以再次请求相同的URL,当然还有另一个GET调用同一个URL,例如用户可以调用" www.google.com"很多时候,前呼叫正在进行中,尚未完成。
我想阻止这种行为,我希望如果在进程中有相同的GET调用,则阻止对同一URL的任何进一步的GET调用。这是我的代码,但我不知道如何实现这个目标:
var request = new RequestInfo(URL) { HttpMethod = "GET" };
//here if the same URL is under process I want to prevent calling request.Send()
if (await request.Send())
{
// other codes
}
答案 0 :(得分:1)
似乎很明显,需要某种挂号呼叫注册表。 不过,在实际实施之前你应该考虑一些事情。
此方法是否应该是线程安全的? 它应该,如果它将同时从多个线程调用。
如果不存在多线程问题,那么简单的Dictionary<string, Task>
(您可能希望在此处使用Task<T>
)就足够了。这是一个可以获得想法的小片段。
private readonly IDictionary<string, Task> _requestRegistry = new Dictionary<string, Task>();
Task MakeRequestAsync(string url)
{
if (_requestRegistry.TryGetValue(url, out var existingTask))
{
return existingTask;
}
var request = new RequestInfo(url) { HttpMethod = "GET" };
var responseTask = request.Send();
_requestRegistry.Add(url, responseTask);
return responseTask;
}
对于并发情况,实现将稍微复杂一些,只要有必要避免共享注册表字段的竞争条件。我们可能希望使用现有仪器而不是手动锁定,因此我们将在此处使用ConcurrentDictionary
类型。代码现在可能与此类似。
private readonly ConcurrentDictionary<string, Task> _requestRegistry = new ConcurrentDictionary<string, Task>();
Task MakeRequestAsync(string url)
{
return _requestRegistry.GetOrAdd(url, _ =>
{
var request = new RequestInfo(url) { HttpMethod = "GET" };
return request.Send();
});
}
但实际上这种方法存在一个小问题 - 您的委托可能会被调用两次,因为当前的并发字典实现会在进行任何锁定之前调用valueFactory
,您可以从source code看到。您应该确定它是否适用于您的方案。
幸运的是,这个问题也有一个简洁而简单的黑客攻击 - 我们的valueFactory
应该变得懒惰。在这种情况下,仍然可以调用valueFactory
两次,但只有Web请求。
private readonly ConcurrentDictionary<string, Lazy<Task>> _requestRegistry = new ConcurrentDictionary<string, Lazy<Task>>();
Task MakeRequestAsync(string url)
{
var lazyRequest = _requestRegistry.GetOrAdd(url, _ => new Lazy<Task>(() =>
{
var request = new RequestInfo(url) {HttpMethod = "GET"};
return request.Send();
}));
return lazyRequest.Value;
}
您认为结果会变得陈旧吗?
如果答案是肯定的,那么由于年龄的原因退回登记处的现有答复是不安全的,只有待处理的任务可能被认为是健康的。这意味着您应该在完成时删除任务。片段再次获得想法
request.Send()
.ContinueWith(_ =>
{
_requestRegistry.Remove(url);
});
您是否希望网址数量有限?
如果没有,将需要实施一些缓存驱逐策略。否则,您可能会为注册表使用过多的内存。
答案 1 :(得分:0)
简单的解决方案就是这样。
public static Dictionary<string, object> URLS = new Dictionary<string, object>();
static void Main(string[] args)
{
Console.WriteLine("Strat");
SendRequest("www.google.com");
Console.WriteLine("1");
SendRequest("www.google.com");
Console.WriteLine("2");
Console.ReadKey();
}
public static async Task SendRequest(string url)
{
if (URLS.ContainsKey(url))
{
lock(URLS[url])
{
Task.Delay(5000).Wait();
//GETASYNC();
}
}
else
{
lock (URLS)
{
URLS.Add(url, new object());
//GETASYNC();
}
}
}