Search code examples
c#.nettask-parallel-libraryblockingportable-class-library

TPL and Monitor in PCL


So I'm writing a client API PCL (.NET 4.5, SL 5, Win8, WP8.1, WP SL 8) library and I've decided that I'm only going to allow one HTTP request at a time. Currently I use the TPL to do them:

Task.Factory.FromAsync<Stream>(httpReq.BeginGetRequestStream, httpReq.EndGetRequestStream, null).ContinueWith<Task<WebResponse>>((requestStreamTask) =>
{
    return Task<WebResponse>.Factory.FromAsync(httpReq.BeginGetResponse, httpReq.EndGetResponse, null);
}).Unwrap().ContinueWith<HttpWebResponse>((getResponseTask) =>
    {
        return (HttpWebResponse)getResponseTask.Result;
    });

So I want to add locking to prevent more than one request from going at once. I know I could simply call Monitor.Enter before I start and call Monitor.Exit in the last ContinueWith. But based on Migrating lock to TPL, I can't use Monitor like that because of threading issues possibly. I have no issue using a different blocking object like that post recommends but as far as I can tell in my PCL the only lock I have available is Monitor.

So what should I do?

EDIT: After talking with Yuval Itzchakov below I've realized the reason I only have the Monitor class for syncing is because I have Silverlight 5 support in my PCL. If there is no other way I'll look into dropping support for SL5 but I'd rather not.

EDIT2: After messing around I realized that I do have the ManualResetEvent class, could I use that?


Solution

  • Since you're writing a PCL and including some older platforms (specifically, SL5), your choices are a bit limited. TPL Dataflow is not supported on SL5, and neither is SemaphoreSlim.

    However, HttpClient is, and so are async/await. These allow your code to be far, far cleaner than Task.Factory.FromAsync + Unwrap + ContinueWith.

    For portable async-ready synchronization and/or producer/consumer queues, I recommend my own AsyncEx library. In this case, an AsyncLock should suffice; it can be used in a similar way to SemaphoreSlim:

    private readonly HttpClient _client = new HttpClient();
    private readonly AsyncLock _mutex = new AsyncLock();
    
    public async Task<string> GetStringAsync(string url)
    {
        using (await _mutex.LockAsync())
        {
            return await _client.GetStringAsync(url);
        }
    }