c# - 在 await 锁定期间 sleep 的正确方法

标签 c# asynchronous windows-runtime httpclient async-await

在我的 win8(winrt、c#)应用程序中,我需要调用一个 Web API,它有一个非常具体的限制:不再每 2 秒调用一次 Web 服务。

我试图强制执行此限制,如下所示:

class Client
{
    const int Delay = 2000;
    HttpClient m_client = new HttpClient();
    int m_ticks = 0;

    public async Task<string> Get(string url)
    {
        // Multiple threads could be calling, I need to protect access to m_ticks:
        string result = null;
        lock (this)
        {
            int ticks = Environment.TickCount - m_ticks;
            if (ticks < Delay)
                await Task.Delay(Delay - ticks);

            result = await m_client.GetStringAsync(url);

            m_ticks = Environment.TickCount;
        }

        return result;
    }
}

这让我碰壁了:

  1. 我不能在锁中使用 await 语句。
  2. 我不能回退到像 WebClient + Thread.Sleep 这样的东西(避免异步废话),因为它在 win8 客户端配置文件中不存在。
  3. 我无法避免此方法是“异步”的,因为如果不在异步函数中,我就无法等待 GetStringAsync 或 Task.Delay。
  4. 我无法避免锁定,因为多个线程可能会调用此函数,而我需要同步访问 m_ticks。

我到底要怎么写这样的东西?

最佳答案

SemaphoreSlim类型在 .NET 4.5 中得到了扩展,以包括 await 兼容的 WaitAsync 方法。它没有基于 IDisposableRelease,但构建一个并不难:

sealed class SemaphoreSlimReleaser : IDisposable
{
  SemaphoreSlim mutex;
  public SemaphoreSlimReleaser(SemaphoreSlim mutex)
  {
    this.mutex = mutex;
  }

  void Dispose()
  {
    if (mutex == null)
      return;
    mutex.Release();
    mutex = null;
  }
}

然后您可以使用与您已有的代码非常相似的代码:

class Client
{
  const int Delay = 2000;
  HttpClient m_client = new HttpClient();
  int m_ticks = 0;
  SemaphoreSlim mutex = new SemaphoreSlim(1);

  public async Task<string> Get(string url)
  {
    // Multiple threads could be calling, I need to protect access to m_ticks:
    string result = null;
    await mutex.WaitAsync();
    using (new SemaphoreSlimReleaser(mutex))
    {
        int ticks = Environment.TickCount - m_ticks;
        if (ticks < Delay)
            await Task.Delay(Delay - ticks);

        result = await m_client.GetStringAsync(url);

        m_ticks = Environment.TickCount;
    }

    return result;
  }
}

附言如果您有兴趣,我的 AsyncEx 库有一个 full suite of async-compatible synchronization primitives灵感来自 Stephen Toub's blog series .

关于c# - 在 await 锁定期间 sleep 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14180915/

相关文章:

c++ - native C++ 将 IIterable 传递给 WinRT

c# - 从 UIElement 对象动态生成 xaml 字符串

c# - 在 C# 中以编程方式更新连接字符串后重新加载 web.config

javascript - 何时将函数标记为异步

javascript - 如何使用 HTML/Javascript 替换 Windows 8 应用程序上应用栏中的按钮?

javascript - Firebase:child_added 完成后如何获取数据

javascript - 在 Array#map 中处理异步/等待中的错误(拒绝)

c# - 如何通过c#代码查找网络上UPNP设备的IP地址(DHCP)

c# - 如何在 MVC3 中获取 trace.axd 的自定义错误页面?

c# - 使用动画的图片框并返回随机值的计时器掷骰子函数