如何使用Task.Delay来控制对Web服务的调用之间的时间跨度 [英] How to use Task.Delay to control time spans between calls to a web service

查看:82
本文介绍了如何使用Task.Delay来控制对Web服务的调用之间的时间跨度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当用户执行特定操作时,将对Web服务进行调用(获取).该服务不允许通话频率超过每秒一次.我当时想我可以使用Task.Delay来控制它,以便以后的调用至少间隔一秒钟,但似乎无法按预期工作. 伪代码如下所示:

When a user takes a certain action a call (get) to a web service is made. 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. 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();
}

调用和继续在同一线程上完成(由Environment.CurrentManagedThreadId报告). 问题是,如果快速连续调用此方法,则两次调用Web服务之间的传递时间将小于1s.我犯了一个愚蠢的错误,或者我不完全了解Task.Delay(或以上两者).有提示吗?

Calls and contiunations are done on same thread (as reported by 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. I've either done a silly mistake or I don't fully understand Task.Delay (or both of the above). 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);
        }
    }
}

输出:

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.

您确实应该使用正确的工具来完成这项工作. SemaphoreSlim怎么样?

You should really be using the right tool for the job. How about a 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);
        }
    }
}

MakeThrottledCall不再根据svick的评论返回Task.

MakeThrottledCall no longer returns Task as per svick's comment.

这篇关于如何使用Task.Delay来控制对Web服务的调用之间的时间跨度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆