1

Итак, я пишу библиотеку клиентских API PCL (.NET 4.5, SL 5, Win8, WP8.1, WP SL 8), и я решил, что я только позволю себе HTTP-запрос за раз. В настоящее время я использую TPL, чтобы сделать их:TPL и монитор в PCL

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.

Итак, что мне делать?

EDIT: После разговора с Юваль Itzchakov ниже я понял причину я только иметь Monitor класс для синхронизации, потому что у меня есть поддержка Silverlight 5 в моем PCL. Если нет другого способа, я рассмотрю отказ от поддержки SL5, но я бы предпочел.

EDIT2: После беспорядка я понял, что у меня есть класс ManualResetEvent, могу ли я использовать его?

+0

Это звучит, как вы можете использовать шаблон производитель/потребитель, где один поток отвечает за все запросы HTTP - вы создаете очередь из них, и поток обрабатывает очередь. Вы можете посмотреть «BlockingCollection » или, возможно, даже поток данных TPL. –

+0

Я искал использование 'BlockingCollection ', но он не поддерживается в конфигурации PCL, которую я использую. То же самое с потоком данных TPL. – shmuelie

ответ

2

Поскольку вы пишете PCL и включаете некоторые старые платформы (в частности, SL5), ваши варианты немного ограничены. TPL Dataflow не поддерживается на SL5, и ни один из них не является SemaphoreSlim.

Однако HttpClient is и so 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); 
    } 
} 
+0

В настоящее время я перехожу к использованию Async/await и 'HttpClient' для библиотеки. Основная причина, по которой я не прыгаю, требует переписывания огромного количества кода, и это не то, что я собираюсь сделать, не задумываясь об этом. Ваш AsyncLock хотя и похож на то, что я хочу, hmmmmmmmmm – shmuelie

+0

@shmuelie: Вы * можете * использовать 'ContinueWith'; просто «ожидание» намного проще. –

+0

Вы имеете в виду, что я могу использовать ваш 'AsyncLock' с' ContinueWith', тогда вы, возможно, выиграете! – shmuelie

1

Прежде всего, в этом ответе я использовал SemaphoreSlim, который указан в documentation, он поддерживается в PCL, поэтому вы можете использовать его вместо Monitor.

Во-вторых, в качестве Jon Skeet pointed out вы можете использовать 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

+0

Я искал использование 'SemaphoreSlim' и TPL Dataflow, но ни один из них не поддерживается в конфигурации PCL, которую у меня есть. – shmuelie

0
.

Вместо того, используя довольно подробный FromAsync шаблон, вы можете использовать HttpClient и SemaphoreSlim (как Арнон и Джон упоминается), которые поддерживают PCL:

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(); 
     } 
} 
+1

Хотя 'SemaphoreSlim' не поддерживается в конфигурации PCL, которую я использую, использование HTTP-клиента - интересная идея. Еще более интересным для меня является то, что для него требуются другие пакеты, которые дают мою конфигурацию Async/Await, которая упростит мою базу кода на стольких уровнях. Так что мне придется это посмотреть. – shmuelie

+0

Что вы подразумеваете под * «Конфигурация PCL im using *"? –

+0

У меня есть набор PCL для поддержки .NET 4.5, Silverlight 5, Windows 8, Windows Phone 8.1 и Windows Phone Silverlight 8. Установив его для всех тех API, которые у меня есть, очень очень ограничительный. – shmuelie