2

私のwin8(winrt、c#)アプリケーションでは、非常に特殊な制限があるWebAPIを呼び出す必要があります。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. Win8クライアントプロファイルに存在しないため、WebClient + Thread.Sleep(非同期ナンセンスを回避)のようなものにフォールバックすることはできません。
  3. このメソッドが「非同期」になるのを避けることはできません。非同期関数を使用しないと、GetStringAsyncまたはTask.Delayを待つことができないためです。
  4. 複数のスレッドがこの関数を呼び出している可能性があり、m_ticksへの同期アクセスが必要なため、ロックを回避することはできません。

いったいどうやってこのようなものを書こうと思うのでしょうか?

4

2 に答える 2

5

SemaphoreSlimタイプはawait.NET4.5で拡張され、互換性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;
  }
}

PS興味があれば、私のAsyncExライブラリには、StephenToubのブログシリーズに触発された互換性のある同期プリミティブの完全なスイートがありasyncます。

于 2013-01-07T02:38:25.763 に答える
3

簡単な解決策:

コンキュレントキューを使用します。

http://msdn.microsoft.com/en-us/library/dd267265.aspx

Webサービスを使用するためのすべてのリクエストがキューに追加されます。

2秒ごとに1つのオブジェクトをエンキューし、それを使用して回答を返すスレッドがあります。

于 2013-01-06T09:47:45.717 に答える