[英]How to use Task.Delay to control time spans between calls to a web service
When a user takes a certain action a call (get) to a web service is made. 当用户采取某种措施时,将调用(获取)Web服务。 The service doesn't allow for calls more frequent than once per second.
该服务不允许通话频率超过每秒一次。 I was thinking I could use Task.Delay to control this so that subsequent calls are made at least one second apart in time but it doesn't seem to work as expected.
我当时想我可以使用Task.Delay来控制它,以便以后的调用至少间隔一秒钟,但似乎无法按预期进行。 The pseudo-code looks like this:
伪代码如下所示:
public async void OnUserAction()
{
var timeUntilPreviousCallWillBeOrWasMade = 1000ms - (Now - previousWaitStartTime);
var timeToWaitBeforeThisCallShouldBeMade = Max(0, timeUntilPreviousCallWillBeOrWasMade + 1000ms);
previousWaitStartTime = Now;
await Task.Delay(timeToWaitBeforeThisCallShouldBeMade);
MakeCallToWebService();
}
Calls and contiunations are done on same thread (as reported by Environment.CurrentManagedThreadId). 调用和继续在同一线程上完成(由Environment.CurrentManagedThreadId报告)。 The problem is that if there's a quick succession of invocations of this method the passed time between calls to the web service is less than 1s.
问题是,如果快速连续调用此方法,则两次调用Web服务之间的传递时间将小于1s。 I've either done a silly mistake or I don't fully understand Task.Delay (or both of the above).
我犯了一个愚蠢的错误,或者我不完全了解Task.Delay(或以上两者)。 Any hints?
有什么提示吗?
It would appear that your problem happens when a request is queued up when another one is already waiting. 当另一个请求已经等待时,当一个请求排队时,就会出现您的问题。 Here's my take on your pseudo code:
这是我对您的伪代码的看法:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ThrottledAsync
{
class Program
{
static void Main(string[] args)
{
// Queue up multiple user actions
// within a short interval.
for (var i = 0; i < 10; i++)
{
OnUserAction();
Thread.Sleep(10);
}
Console.ReadLine();
}
private static int UserActionID;
private static DateTime previousWaitStartTime;
public static async void OnUserAction()
{
// Keep track of the operation ID.
var userActionID = Interlocked.Increment(ref UserActionID);
// Pseudo-code implementation.
var timeUntilPreviousCallWillBeOrWasMade = 1000 - (int)(DateTime.Now.Subtract(previousWaitStartTime).TotalMilliseconds);
Console.WriteLine(
"{0:HH:mm:ss.ffff} - User action {1}: timeUntilPreviousCallWillBeOrWasMade = {2}.",
DateTime.Now, userActionID, timeUntilPreviousCallWillBeOrWasMade);
var timeToWaitBeforeThisCallShouldBeMade = Math.Max(0, timeUntilPreviousCallWillBeOrWasMade + 1000);
Console.WriteLine(
"{0:HH:mm:ss.ffff} - User action {1}: timeToWaitBeforeThisCallShouldBeMade = {2}.",
DateTime.Now, userActionID, timeToWaitBeforeThisCallShouldBeMade);
previousWaitStartTime = DateTime.Now;
await Task.Delay(timeToWaitBeforeThisCallShouldBeMade);
await MakeCallToWebService(userActionID);
}
private static async Task MakeCallToWebService(int userActionID)
{
// Simulate network delay.
await Task.Delay(new Random().Next(5, 10));
Console.WriteLine("{0:HH:mm:ss.ffff} - User action {1}: web service call.", DateTime.Now, userActionID);
}
}
}
And the output: 并输出:
19:10:11.1366 - User action 1: timeUntilPreviousCallWillBeOrWasMade = -2147482648.
19:10:11.1416 - User action 1: timeToWaitBeforeThisCallShouldBeMade = 0.
19:10:11.1536 - User action 2: timeUntilPreviousCallWillBeOrWasMade = 988.
19:10:11.1536 - User action 2: timeToWaitBeforeThisCallShouldBeMade = 1988.
19:10:11.1586 - User action 1: web service call.
19:10:11.1646 - User action 3: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.1646 - User action 3: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.1756 - User action 4: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.1756 - User action 4: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.1866 - User action 5: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.1866 - User action 5: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.1976 - User action 6: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.1986 - User action 6: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.2086 - User action 7: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.2086 - User action 7: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.2186 - User action 8: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.2196 - User action 8: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.2296 - User action 9: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.2296 - User action 9: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:11.2406 - User action 10: timeUntilPreviousCallWillBeOrWasMade = 990.
19:10:11.2406 - User action 10: timeToWaitBeforeThisCallShouldBeMade = 1990.
19:10:13.1567 - User action 2: web service call.
19:10:13.1717 - User action 3: web service call.
19:10:13.1877 - User action 5: web service call.
19:10:13.1877 - User action 4: web service call.
19:10:13.2107 - User action 6: web service call.
19:10:13.2187 - User action 7: web service call.
19:10:13.2187 - User action 8: web service call.
19:10:13.2357 - User action 9: web service call.
19:10:13.2537 - User action 10: web service call.
You should really be using the right tool for the job. 您确实应该为该工作使用正确的工具。 How about a
SemaphoreSlim
? SemaphoreSlim
怎么样?
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ThrottledAsync
{
class Program
{
static void Main(string[] args)
{
// Queue up simultaneous calls.
MakeThrottledCall();
MakeThrottledCall();
MakeThrottledCall();
MakeThrottledCall();
Console.ReadLine();
}
// Used to throttle our web service calls.
// Max degree of parallelism: 1.
private static readonly SemaphoreSlim WebServiceMutex = new SemaphoreSlim(1, 1);
private static async void MakeThrottledCall()
{
// Wait for the previous call
// (and delay task) to complete.
await WebServiceMutex.WaitAsync();
try
{
await MakeCallToWebService();
// Report the completion of your web service call if necessary.
// Delay for a bit before releasing the semaphore.
await Task.Delay(1000);
}
finally
{
// Allow the next web service call to go through.
WebServiceMutex.Release();
}
}
private static async Task MakeCallToWebService()
{
// Simulate network delay.
await Task.Delay(new Random().Next(5, 10));
Console.WriteLine("WebServiceCall: {0:HH:mm:ss.ffff}", DateTime.Now);
}
}
}
EDIT: MakeThrottledCall
no longer returns Task
as per svick's comment. 编辑:
MakeThrottledCall
不再根据svick的评论返回Task
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.