2013-01-06 141 views
2

在我的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. 在锁我不能使用等待声明。
  2. 我不能回退到像WebClient + Thread.Sleep(避免异步废话),因为它不存在于win8客户端配置文件。
  3. 我不能避免这种方法是“异步”,因为然后我不能等待GetStringAsync或Task.Delay而不处于异步函数。
  4. 我无法避免锁定,因为多个线程可能正在调用此函数,并且我需要对m_ticks进行同步访问。

在这个世界上我该怎么写这样的东西?

回答

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; 
    } 
} 

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

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

+0

为什么你在'Dispose'上设置'mutex'为空有特定的原因吗?如果你想为其他并发操作重用'SemaphoreSlim'会怎么样? – TopinFrassi

+1

@TopinFrassi:“SemaphoreSlimReleaser”只是表示信号量上的一个“获取/释放”,而不是信号量本身。我将成员变量设置为'null',以便多个处置无效。 –

+0

哦,对,我现在看到。谢谢! – TopinFrassi