C# 如何使用Task.Delay控制对web服务的调用之间的时间跨度

C# 如何使用Task.Delay控制对web服务的调用之间的时间跨度,c#,windows-runtime,async-await,winrt-async,C#,Windows Runtime,Async Await,Winrt Async,当用户执行某个操作时,会调用get到web服务。该服务不允许通话频率超过每秒一次。我在想我可以使用Task.Delay来控制它,这样以后的调用在时间上至少间隔一秒,但它似乎并没有按预期工作。 伪代码如下所示: public async void OnUserAction() { var timeUntilPreviousCallWillBeOrWasMade = 1000ms - (Now - previousWaitStartTime); var timeToWaitBefor

当用户执行某个操作时,会调用get到web服务。该服务不允许通话频率超过每秒一次。我在想我可以使用Task.Delay来控制它,这样以后的调用在时间上至少间隔一秒,但它似乎并没有按预期工作。 伪代码如下所示:

public async void OnUserAction()
{
    var timeUntilPreviousCallWillBeOrWasMade = 1000ms - (Now - previousWaitStartTime);
    var timeToWaitBeforeThisCallShouldBeMade = Max(0, timeUntilPreviousCallWillBeOrWasMade + 1000ms);
    previousWaitStartTime = Now;
    await Task.Delay(timeToWaitBeforeThisCallShouldBeMade);
    MakeCallToWebService();
}
调用和连接在Environment.CurrentManagedThreadId报告的同一线程上完成。
问题是,如果此方法的调用很快连续进行,则对web服务的调用之间的传递时间少于1s。我要么犯了一个愚蠢的错误,要么我不完全理解这项任务。延迟或以上两者兼而有之。有什么提示吗?

您可以使用Thread.sleep毫秒刺激吗


我建议您使用它。

当一个请求排队时,另一个请求已经在等待时,您的问题就会出现。以下是我对您的伪代码的看法:

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);
        }
    }
}
你真的应该为这项工作使用正确的工具。信号量Lim怎么样


编辑:MakeThrottledCall不再根据svick的评论返回任务。

Thread.Sleep在.NET for Windows应用商店应用程序中不可用。。。。即使是这样,它也不能解决问题,因为Task.Delay和Thread.Sleep在等待一定时间后提供相同的功能,但Thread.Sleep会阻止线程,这正是我们在使用任务和异步/等待构造时想要避免的。我不喜欢你的解决方案,因为MakeTrottledCall应该更准确地完成,所以它返回的任务应该在MakeCallToWebService完成后立即完成,而不是一秒钟后完成。@svick,你说得很对。我没有一个很好的解决方案-只有一些丑陋的黑客行为,比如对Task.Delay1000.ContinueWith=>Semaphore.Release的调用,比如一场火灾,然后忘记;所描述的解决方案通常不是广泛适用于异步节流的模式。但是,它确实解决了问题所描述的具体情况,您会注意到原始OnUserAction是一个空的,所以我现在坚持使用它。感谢您提出的替代解决方案,我将尝试它。但是,您对后续web服务调用之间的时间间隔的假设是不正确的。因为它表示为Max0,timeuntilpreviouscallbeorwasmake+1000ms,所以最近调用的调用的等待时间至少为1000ms(当以前的调用尚未发出时),而小于1000ms(当已经发出时)。所以问题仍然存在,为什么后续呼叫之间的时间有时小于1000毫秒?@Christian,我错过了+1000毫秒。然而,所有这些变化都是第一次和第二次并发调用之间的时间——之后它们开始像机关枪一样开火。您只考虑上一次呼叫的等待开始,这并不能为您提供完整的情况。当两个以上的请求彼此非常接近时,previousWaitStartTime会多次被设置为大致相同的值,然后根据它进行计算,最终导致多个延迟任务在大致相同的时间内流逝。啊,当然,我现在觉得有点傻了。我想我只需要考虑一下以前的电话,当然这不是真的,因为我在用时间差来计算。我用+++\u修改了你的代码;等待任务。延迟时间*1000-_消费;这给出了更合理的数字。谢谢!
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.
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);
        }
    }
}