我有大量的图片我尝试下载(然后调整大小并保存)。我试图以最有效的方式做到这一点。我选择使用BlockingCollection和一个下载图像的制作人和一个消费者,这些消费者会在下载后调整大小并保存图像。
我遇到的主要问题是下载图像的制作人。我使用SemaphoreSlim来排队下载任务,从输出中我可以告诉它更快,更好,更异步地工作。我将添加到任务列表中,然后使用Task.WaitAll
等待所有下载完成,然后再完成整个过程。问题是WaitAll
似乎并没有等待所有任务完成,然后才能从此屏幕截图中看到:
这是我的虚拟代码,它将重现问题:
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
我认为这可能是我发布信号量或其他东西的问题,但我似乎无法弄清问题是什么。