diff --git a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs index 1367980..4a6ac00 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs @@ -7,7 +7,7 @@ public sealed class ScrapingClientService : IScrapingClientService, IDisposable { private readonly IHttpClientFactory _httpClientFactory; private readonly PeriodicTimer _periodicTimer; - private readonly Queue> _actionQueue = []; + private readonly Queue> _actionQueue = []; private bool _workerActivated; public ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider timeProvider) @@ -18,86 +18,101 @@ public ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider Worker(); } - public void Dispose() + public Task GetAsStringAsync(string url, CancellationToken ct) { - _periodicTimer.Dispose(); - } + var taskCompletion = new TaskCompletionSource(); - private async void Worker() - { lock (_actionQueue) - { - _workerActivated = true; - } - - while (await _periodicTimer.WaitForNextTickAsync().ConfigureAwait(false) && _actionQueue.Count > 0) - { - if (_actionQueue.TryDequeue(out var action)) + _actionQueue.Enqueue(async httpClient => { - await action(_httpClientFactory.CreateClient()).ConfigureAwait(false); - } - } + if (ct.IsCancellationRequested) + taskCompletion.SetCanceled(ct); + + try + { + var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); + taskCompletion.SetResult(await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false)); + } + catch (Exception ex) + { + taskCompletion.SetException(ex); + } + }); + + EnsureWorkerActivated(); - lock (_actionQueue) - { - _workerActivated = false; - } + return taskCompletion.Task; } - public Task GetAsStringAsync(string url, CancellationToken ct) + public Task GetAsStreamAsync(string url, Stream destination, CancellationToken ct) { - var taskCompletion = new TaskCompletionSource(); - _actionQueue.Enqueue(async httpClient => - { - if (ct.IsCancellationRequested) - taskCompletion.SetCanceled(ct); - - try - { - var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); - taskCompletion.SetResult(await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false)); - } - catch (Exception ex) - { - taskCompletion.SetException(ex); - } - }); + var taskCompletion = new TaskCompletionSource(); lock (_actionQueue) - { - if (!_workerActivated) - Worker(); - } + _actionQueue.Enqueue(async httpClient => + { + if (ct.IsCancellationRequested) + taskCompletion.SetCanceled(ct); + + try + { + var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); + await response.Content.CopyToAsync(destination, ct).ConfigureAwait(false); + taskCompletion.SetResult(response.Content.Headers.ContentDisposition); + } + catch (Exception ex) + { + taskCompletion.SetException(ex); + } + }); + + EnsureWorkerActivated(); return taskCompletion.Task; } - public Task GetAsStreamAsync(string url, Stream destination, CancellationToken ct) + /// + /// が起動していない場合は起動します + /// + private void EnsureWorkerActivated() { - var taskCompletion = new TaskCompletionSource(); - _actionQueue.Enqueue(async httpClient => - { - if (ct.IsCancellationRequested) - taskCompletion.SetCanceled(ct); + bool activateWorker; + lock (_actionQueue) activateWorker = !_workerActivated; - try - { - var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); - await response.Content.CopyToAsync(destination, ct).ConfigureAwait(false); - taskCompletion.SetResult(response.Content.Headers.ContentDisposition); - } - catch (Exception ex) - { - taskCompletion.SetException(ex); - } - }); + if (activateWorker) + Worker(); + } + /// + /// のConsumer + /// 別スレッドでループさせるためにvoid + /// + private async void Worker() + { lock (_actionQueue) + _workerActivated = true; + + try { - if (!_workerActivated) - Worker(); + while (await _periodicTimer.WaitForNextTickAsync().ConfigureAwait(false) && _actionQueue.Count > 0) + { + Func? action; + lock (_actionQueue) + if (!_actionQueue.TryDequeue(out action)) + continue; + + await action(_httpClientFactory.CreateClient()).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + } } + finally + { + lock (_actionQueue) + _workerActivated = false; + } + } - return taskCompletion.Task; + public void Dispose() + { + _periodicTimer.Dispose(); } }