在等待锁定期间正确睡觉的方法

LCC*_*LCC 2 c# asynchronous httpclient async-await windows-runtime

在我的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这让我陷入了困境:

  1. 我不能在锁中使用await语句.
  2. 我不能回到WebClient + Thread.Sleep(避免异步废话)之类的东西,因为它在win8客户端配置文件中不存在.
  3. 我不能避免这种方法是"异步",因为那样我就不能在没有异步函数的情况下等待GetStringAsync或Task.Delay.
  4. 我无法避免锁定,因为多个线程可能正在调用此函数,我需要同步访问m_ticks.

我怎么会这样写这样的东西?

Ste*_*ary 5

SemaphoreSlim类型在.NET 4.5中扩展为包含await兼容的WaitAsync方法.它没有IDisposable基础Release,但建立一个并不难:

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

  void Dispose()
  {
    if (mutex == null)
      return;
    mutex.Release();
    mutex = null;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

PS如果您有兴趣,我的AsyncEx库有一整套async兼容的同步原语,灵感来自Stephen Toub的博客系列.