使用SemaphoreSlim

时间:2018-04-24 21:29:20

标签: c# .net asynchronous asp.net-core producer-consumer

我有大量的图片我尝试下载(然后调整大小并保存)。我试图以最有效的方式做到这一点。我选择使用BlockingCollection和一个下载图像的制作人和一个消费者,这些消费者会在下载后调整大小并保存图像。

我遇到的主要问题是下载图像的制作人。我使用SemaphoreSlim来排队下载任务,从输出中我可以告诉它更快,更好,更异步地工作。我将添加到任务列表中,然后使用Task.WaitAll等待所有下载完成,然后再完成整个过程。问题是WaitAll似乎并没有等待所有任务完成,然后才能从此屏幕截图中看到:

enter image description here

这是我的虚拟代码,它将重现问题:

class Program
{
    static IDictionary<string, string> imageUrls;
    static BlockingCollection<Image> queue;

    static void Main(string[] args)
    {
        imageUrls = new Dictionary<string, string>();
        for (int i = 0; i < 20; i++)
        {
            imageUrls.Add($"{i}-1", "https://emergingpayments.org/wp-content/uploads/2017/11/landscape-2.jpeg");
            imageUrls.Add($"{i}-2", "https://static.photocdn.pt/images/articles/2018/03/09/articles/2017_8/landscape_photography.jpg");
            imageUrls.Add($"{i}-3", "https://wallup.net/wp-content/uploads/2015/12/258088-sunset-landscape-horizon.jpg");
        }

        queue = new BlockingCollection<Image>();

        Task.WaitAll(
            Task.Run(() => processImages()),
            Task.Run(() => downloadImages())
        );
    }

    static async Task downloadImages()
    {
        using (var client = new HttpClient(new HttpClientHandler { MaxConnectionsPerServer = 100 }))
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            using (var semaphore = new SemaphoreSlim(20))
            {
                var downloadTasks = new List<Task>();
                foreach (var imageUrl in imageUrls)
                {
                    await semaphore.WaitAsync();
                    downloadTasks.Add(Task.Factory.StartNew(async () =>
                    {
                        try
                        {
                            var imageData = await client.GetByteArrayAsync(imageUrl.Value);
                            queue.Add(new Image { Id = imageUrl.Key, ImageData = imageData });
                            Console.WriteLine($"Downloaded image {imageUrl.Key}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Download failed for image {imageUrl.Key} - {ex.Message}");
                        }
                        finally
                        {
                            semaphore.Release();
                        }
                    }));
                }

                Task.WaitAll(downloadTasks.ToArray());
                Console.WriteLine($"Downloading took {stopwatch.Elapsed.TotalSeconds} seconds");
            }
        }            
    }

    static void processImages()
    {
        while (!queue.IsCompleted)
        {
            Image image = null;
            try
            {
                image = queue.Take();
            }
            catch (InvalidOperationException) { }

            if (image != null)
            {
                Thread.Sleep(100);
                Console.WriteLine($"Processed image {image.Id}");
                Console.WriteLine($"Processed image {image.Id}");
                Console.WriteLine($"Processed image {image.Id}");
            }
        }
    }

    class Image
    {
        public string Id { get; set; }
        public byte[] ImageData { get; set; }
    }
}

我的解决方案与此类似:https://stackoverflow.com/a/36571420/5392786

我认为这可能是我发布信号量或其他东西的问题,但我似乎无法弄清问题是什么。

0 个答案:

没有答案