PCL中的TPL和监视器

时间:2014-11-27 06:47:11

标签: c# .net task-parallel-library blocking portable-class-library

所以我正在编写客户端API PCL(.NET 4.5,SL 5,Win8,WP8.1,WP SL 8)库,我决定一次只允许一个HTTP请求。目前我使用TPL来完成它们:

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

所以我想添加锁定以防止一次发出多个请求。我知道我可以在开始之前致电Monitor.Enter并在最后Monitor.Exit中致电ContinueWith。但是基于Migrating lock to TPL,由于可能存在线程问题,我无法使用Monitor。我没有使用像帖子推荐的那样使用不同的阻塞对象,但据我所知,我的PCL中唯一可用的锁是Monitor。

那我该怎么办?

编辑:在与Yuval Itzchakov交谈后,我意识到我只有Monitor课程进行同步的原因是因为我的PCL支持Silverlight 5。如果没有别的办法,我会考虑放弃对SL5的支持,但我宁愿不这样做。

EDIT2:在搞砸了我意识到我确实拥有ManualResetEvent课程后,我可以使用它吗?

3 个答案:

答案 0 :(得分:2)

由于您正在编写PCL并包含一些较旧的平台(特别是SL5),因此您的选择有限。 SL5不支持TPL数据流,SemaphoreSlim也不支持。

但是,HttpClient isso are async/await。这些允许您的代码远远超过Task.Factory.FromAsync + Unwrap + ContinueWith

对于便携式async - 就绪同步和/或生产者/消费者队列,我推荐自己的AsyncEx library。在这种情况下,AsyncLock就足够了;它可以与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);
    }
}

答案 1 :(得分:1)

首先,在该答案中,我使用SemaphoreSlim在PCL支持的documentation中说明,因此您可以使用它代替Monitor

其次,作为Jon Skeet pointed out,您可以使用TPL数据流ActionBlock(PCL也支持async-await

var block = new ActionBlock<HttpWebRequest>(request => 
{
    var result = await request.GetResponseAsync();
    // handle result
}

HttpWebRequest newRequest = // ...
block.Post(newRequest);

该块一次处理一个请求,并使用Post添加新的onces。

答案 2 :(得分:0)

而不是使用相当冗长的FromAsync模式,您可以使用支持PCL的HttpClientSemaphoreSlim(如Arnon和Jon所提到的):

private SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);

private HttpClient _httpClient = new HttpClient();

public async Task SendRequestAsync(HttpWebRequest request)
{
      await _semaphoreSlim.WaitAsync();
      try
      {
         return await _httpClient.SendAsync(request);
      }
      finally
      {
         _semaphoreSlim.Release();
      }
}