0

当用户采取特定操作时,会调用(获取)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 服务之间的传递时间小于 1 秒。我要么犯了一个愚蠢的错误,要么我不完全理解 Task.Delay (或以上两者)。有什么提示吗?

4

2 回答 2

2

当另一个请求已经在等待时,当一个请求排队时,您的问题似乎就会发生。这是我对您的伪代码的看法:

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

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不再Task根据 svick 的评论返回。

于 2013-06-15T01:07:58.950 回答
-4

你可以在msdn使用 Thread.Sleep(millisecondsTimeout)吗?

我建议你使用它。

于 2013-06-15T00:43:55.650 回答