PCL 中的 TPL 和监视器
本文关键字:监视器 TPL 中的 PCL | 更新日期: 2023-09-27 18:32:07
所以我正在编写一个客户端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
,并在最后一ContinueWith
打电话给Monitor.Exit
。但是基于将锁定迁移到 TPL,由于线程问题,我无法使用这样的Monitor
。像该帖子建议的那样,使用不同的阻止对象我没有问题,但据我所知,在我的 PCL 中,我唯一可用的锁是监视器。
那我该怎么办?
编辑:在下面与Yuval Itzchakov交谈后,我意识到我只有Monitor
同步类的原因是因为我的PCL中有Silverlight 5支持。如果没有其他方法,我会考虑放弃对 SL5 的支持,但我宁愿不这样做。
编辑2:在搞砸之后,我意识到我确实有ManualResetEvent
课,我可以使用它吗?
由于您正在编写 PCL 并包含一些较旧的平台(特别是 SL5),因此您的选择有点有限。SL5 不支持 TPL 数据流,SemaphoreSlim
也不支持 TPL 数据流。
但是,HttpClient
是,async
/await
也是如此。这些使您的代码比 Task.Factory.FromAsync
+ Unwrap
+ ContinueWith
干净得多。
对于可移植async
就绪的同步和/或生产者/消费者队列,我推荐我自己的 AsyncEx 库。在这种情况下,一个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);
}
}
首先,在该答案中,我使用了文档中声明PCL支持SemaphoreSlim
,因此您可以使用它而不是Monitor
。
其次,正如Jon Skeet指出的那样,您可以使用TPL Dataflow的ActionBlock
(在PCL(中也支持async-await
:
var block = new ActionBlock<HttpWebRequest>(request =>
{
var result = await request.GetResponseAsync();
// handle result
}
HttpWebRequest newRequest = // ...
block.Post(newRequest);
该块一次处理一个所有请求,您可以使用 Post
添加新请求一次。
与其使用非常冗长的FromAsync
模式,不如使用支持 PCL 的 HttpClient
和 SemaphoreSlim
(如 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();
}
}