Разрабатывая Windows-службу, работающую с МФУ и принтерами по HTTP, я использовал класс HttpClient. Данный класс является потоко-безопасным и не требует создания многочисленных экземпляров: достаточно один раз создать HttpClient, чтобы он обсужил все требующиеся запросы (в том числе многопоточные):
1 | private static readonly HttpClient Client = new HttpClient(); |
или если не требуется, чтобы использовались Cookies:
1 | private static readonly HttpClient Client = new HttpClient(new HttpClientHandler { UseCookies = false }); |
Данный подход избавит от проблемной утилизации (Dispose) экземпляров HttpClient и потенциального исчерпания свободных портов в системе (port exhausting), связанного с проблемной утилизацией.
Всё бы хорошо, но мною была обнаружена проблема разделяемого общего состояния эксземпляра класса HttpClient, когда мне потребовалось кроме сообщений ещё и отправлять Http Headers с аутентификацией и служебной информацией.
Оказалось, что мною используемый подход:
1 | Client.DefaultRequestHeaders.Clear(); // doesn't work for concurrency |
некореектно работает в многопоточной среде, т.к. имеет разделяемое общее состояние хэдеров Http Headers.
Исследование проблемы натолкнуло меня на отличную статью с решением данной проблемы: Concurrency with HttpClient
Решение заключается в отказе от внутреннего разделяемого общего состояния HttpClient’а, а именно от Default Header. Следует дописать метод расширения, который делает то же самое, что и вышеобозначенный код, но без разделяемого общего состояния:
1 | public static class HttpClientExtensions |
Теперь вызовы из кода HttpClient с передачей хэдеров Http Headers выглядят так и являются потоко-безопасными:
1 | var response = await Client.GetAsyncExt(requestUri, h => |