C# 并行任务库WaitAny设计
我刚刚开始探索PTL,有一个设计问题 我的设想: 我有一个URL列表,每个URL都引用一个图像。我希望每个图像都可以并行下载。至少下载一个图像后,我希望执行一个方法,对下载的图像执行某些操作。这种方法不应该是并行的——它应该是串行的 我认为下面的方法可行,但我不确定这是否是正确的方法。因为我有单独的类来收集图像和处理收集的图像,所以我最终传递了一系列任务,这些任务似乎是错误的,因为它暴露了图像检索的内部工作方式。但我不知道该怎么办。实际上,这两种方法都有更多的优点,但这并不重要。要知道,它们真的不应该被集中到一个既检索图像又处理图像的大型方法中C# 并行任务库WaitAny设计,c#,task-parallel-library,C#,Task Parallel Library,我刚刚开始探索PTL,有一个设计问题 我的设想: 我有一个URL列表,每个URL都引用一个图像。我希望每个图像都可以并行下载。至少下载一个图像后,我希望执行一个方法,对下载的图像执行某些操作。这种方法不应该是并行的——它应该是串行的 我认为下面的方法可行,但我不确定这是否是正确的方法。因为我有单独的类来收集图像和处理收集的图像,所以我最终传递了一系列任务,这些任务似乎是错误的,因为它暴露了图像检索的内部工作方式。但我不知道该怎么办。实际上,这两种方法都有更多的优点,但这并不重要。要知道,它们真的
//From the Director class
Task<Image>[] downloadTasks = collector.RetrieveImages(listOfURLs);
for (int i = 0; i < listOfURLs.Count; i++)
{
//Wait for any of the remaining downloads to complete
int completedIndex = Task<Image>.WaitAny(downloadTasks);
Image completedImage = downloadTasks[completedIndex].Result;
//Now do something with the image (this "something" must happen serially)
//Uses the "Formatter" class to accomplish this let's say
}
///////////////////////////////////////////////////
//From the Collector class
public Task<Image>[] RetrieveImages(List<string> urls)
{
Task<Image>[] tasks = new Task<Image>[urls.Count];
int index = 0;
foreach (string url in urls)
{
string lambdaVar = url; //Required... Bleh
tasks[index] = Task<Image>.Factory.StartNew(() =>
{
using (WebClient client = new WebClient())
{
//TODO: Replace with live image locations
string fileName = String.Format("{0}.png", i);
client.DownloadFile(lambdaVar, Path.Combine(Application.StartupPath, fileName));
}
return Image.FromFile(Path.Combine(Application.StartupPath, fileName));
},
TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent);
index++;
}
return tasks;
}
实现这一点的最佳方法可能是实现Observer模式:让您的RetreiveImages函数实现,将完成的图像操作放入对象的OnNext方法中,然后将其订阅到RetreiveImages
我自己还没有尝试过这个,但我仍然需要更多地使用任务库,但我认为这是正确的方法 最好的方法可能是实现观测者模式:让您的RetreiveImages函数实现,将完成的图像操作放入对象的OnNext方法中,然后将其订阅到RetreiveImages
我自己还没有尝试过这个,但我仍然需要更多地使用任务库,但我认为这是正确的方法 TPL已经提供了ContinueWith函数,可以在一个任务完成时执行另一个任务。任务链是TPL中用于异步操作的主要模式之一 下面的方法下载一组图像,并通过重命名每个文件继续
static void DownloadInParallel(string[] urls)
{
var tempFolder = Path.GetTempPath();
var downloads = from url in urls
select Task.Factory.StartNew<string>(() =>{
using (var client = new WebClient())
{
var uri = new Uri(url);
string file = Path.Combine(tempFolder,uri.Segments.Last());
client.DownloadFile(uri, file);
return file;
}
},TaskCreationOptions.LongRunning|TaskCreationOptions.AttachedToParent)
.ContinueWith(t=>{
var filePath = t.Result;
File.Move(filePath, filePath + ".test");
},TaskContinuationOptions.ExecuteSynchronously);
var results = downloads.ToArray();
Task.WaitAll(results);
}
TPL已经提供了ContinueWith函数,可以在一个任务完成时执行另一个任务。任务链是TPL中用于异步操作的主要模式之一 下面的方法下载一组图像,并通过重命名每个文件继续
static void DownloadInParallel(string[] urls)
{
var tempFolder = Path.GetTempPath();
var downloads = from url in urls
select Task.Factory.StartNew<string>(() =>{
using (var client = new WebClient())
{
var uri = new Uri(url);
string file = Path.Combine(tempFolder,uri.Segments.Last());
client.DownloadFile(uri, file);
return file;
}
},TaskCreationOptions.LongRunning|TaskCreationOptions.AttachedToParent)
.ContinueWith(t=>{
var filePath = t.Result;
File.Move(filePath, filePath + ".test");
},TaskContinuationOptions.ExecuteSynchronously);
var results = downloads.ToArray();
Task.WaitAll(results);
}
通常,当您不关心任何其他任务的结果时,使用WaitAny来等待一个任务。例如,如果您只是关心第一个碰巧返回的图像 这个怎么样 这将创建两个任务,一个任务加载图像并将其添加到块集合。第二个任务等待集合并处理添加到队列中的所有图像。加载所有映像后,第一个任务关闭队列,以便第二个任务可以关闭
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class Class1
{
readonly string _path = Directory.GetCurrentDirectory();
public void Demo()
{
IList<string> listOfUrls = new List<string>();
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/editicon.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/favorite-star-on.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/arrow_dsc_green.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/editicon.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/favorite-star-on.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/arrow_dsc_green.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/editicon.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/favorite-star-on.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/arrow_dsc_green.gif");
BlockingCollection<Image> images = new BlockingCollection<Image>();
Parallel.Invoke(
() => // Task 1: load the images
{
Parallel.For(0, listOfUrls.Count, (i) =>
{
Image img = RetrieveImages(listOfUrls[i], i);
img.Tag = i;
images.Add(img); // Add each image to the queue
});
images.CompleteAdding(); // Done with images.
},
() => // Task 2: Process images serially
{
foreach (var img in images.GetConsumingEnumerable())
{
string newPath = Path.Combine(_path, String.Format("{0}_rot.png", img.Tag));
Console.WriteLine("Rotating image {0}", img.Tag);
img.RotateFlip(RotateFlipType.RotateNoneFlipXY);
img.Save(newPath);
}
});
}
public Image RetrieveImages(string url, int i)
{
using (WebClient client = new WebClient())
{
string fileName = Path.Combine(_path, String.Format("{0}.png", i));
Console.WriteLine("Downloading {0}...", url);
client.DownloadFile(url, Path.Combine(_path, fileName));
Console.WriteLine("Saving {0} as {1}.", url, fileName);
return Image.FromFile(Path.Combine(_path, fileName));
}
}
}
}
警告:代码没有任何错误检查或取消。很晚了,你需要做点什么,对吗
这是管道模式的一个示例。它假设获取图像的速度非常慢,并且在阻塞集合内部锁定的成本不会引起问题,因为与下载图像所花费的时间相比,锁定发生的频率相对较低
我们的书。。。您可以阅读更多关于此模式和其他并行编程模式的信息
第7章介绍了管道,附带的示例显示了带有错误处理和取消的管道。通常,当您不关心其他任务的结果时,您可以使用WaitAny来等待一个任务。例如,如果您只是关心第一个碰巧返回的图像 这个怎么样 这将创建两个任务,一个任务加载图像并将其添加到块集合。第二个任务等待集合并处理添加到队列中的所有图像。加载所有映像后,第一个任务关闭队列,以便第二个任务可以关闭
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace ClassLibrary1
{
public class Class1
{
readonly string _path = Directory.GetCurrentDirectory();
public void Demo()
{
IList<string> listOfUrls = new List<string>();
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/editicon.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/favorite-star-on.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/arrow_dsc_green.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/editicon.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/favorite-star-on.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/arrow_dsc_green.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/editicon.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/favorite-star-on.gif");
listOfUrls.Add("http://i3.codeplex.com/Images/v16821/arrow_dsc_green.gif");
BlockingCollection<Image> images = new BlockingCollection<Image>();
Parallel.Invoke(
() => // Task 1: load the images
{
Parallel.For(0, listOfUrls.Count, (i) =>
{
Image img = RetrieveImages(listOfUrls[i], i);
img.Tag = i;
images.Add(img); // Add each image to the queue
});
images.CompleteAdding(); // Done with images.
},
() => // Task 2: Process images serially
{
foreach (var img in images.GetConsumingEnumerable())
{
string newPath = Path.Combine(_path, String.Format("{0}_rot.png", img.Tag));
Console.WriteLine("Rotating image {0}", img.Tag);
img.RotateFlip(RotateFlipType.RotateNoneFlipXY);
img.Save(newPath);
}
});
}
public Image RetrieveImages(string url, int i)
{
using (WebClient client = new WebClient())
{
string fileName = Path.Combine(_path, String.Format("{0}.png", i));
Console.WriteLine("Downloading {0}...", url);
client.DownloadFile(url, Path.Combine(_path, fileName));
Console.WriteLine("Saving {0} as {1}.", url, fileName);
return Image.FromFile(Path.Combine(_path, fileName));
}
}
}
}
警告:代码没有任何错误检查或取消。很晚了,你需要做点什么,对吗
这是管道模式的一个示例。它假设获取图像的速度非常慢,并且在阻塞集合内部锁定的成本不会引起问题,因为与下载图像所花费的时间相比,锁定发生的频率相对较低
我们的书。。。您可以阅读更多关于此模式和其他并行编程模式的信息
第7章介绍了管道,附带的示例显示了带有错误处理和取消的管道。//下载所有图像
private async void GetAllImages ()
{
var downloadTasks = listOfURLs.Where(url => !string.IsNullOrEmpty(url)).Select(async url =>
{
var ret = await RetrieveImage(url);
return ret;
}).ToArray();
var counts = await Task.WhenAll(downloadTasks);
}
//From the Collector class
public async Task<Image> RetrieveImage(string url)
{
var lambdaVar = url; //Required... Bleh
using (WebClient client = new WebClient())
{
//TODO: Replace with live image locations
var fileName = String.Format("{0}.png", i);
await client.DownloadFile(lambdaVar, Path.Combine(Application.StartupPath, fileName));
}
return Image.FromFile(Path.Combine(Application.StartupPath, fileName));
}
//下载所有图片
private async void GetAllImages ()
{
var downloadTasks = listOfURLs.Where(url => !string.IsNullOrEmpty(url)).Select(async url =>
{
var ret = await RetrieveImage(url);
return ret;
}).ToArray();
var counts = await Task.WhenAll(downloadTasks);
}
//From the Collector class
public async Task<Image> RetrieveImage(string url)
{
var lambdaVar = url; //Required... Bleh
using (WebClient client = new WebClient())
{
//TODO: Replace with live image locations
var fileName = String.Format("{0}.png", i);
await client.DownloadFile(lambdaVar, Path.Combine(Application.StartupPath, fileName));
}
return Image.FromFile(Path.Combine(Application.StartupPath, fileName));
}
我想这可能是因为我试图同时学习PTL和Observer模式,但我似乎无法正确理解。如果我展示了Collector类、Director类顶级代码片段和Formatter类做一些事情,我将如何实现它?我想这可能是因为我试图同时学习PTL和Observer模式,但我似乎做得不对。如果我有催收课
如图所示,Director类是顶级代码片段,Formatter类是执行某些操作的对象,我将如何实现它?有两件事,我不认为TaskContinuationOptions.ExecuteSynchronously执行我需要的操作。在您的示例中,移动文件的操作不能在多个线程上同时发生。让我们假设它不是移动文件,而是通过串行电缆与设备通信。第二,就像我说的,它不仅仅是我简化的。我认为把这两项任务合并成一种方法是不合适的。但这迫使我绕过看起来像是坏模式的任务。你所说的坏模式是TPL的实际设计理念。在这个问题上,它实际上非常接近F。其次,ExecuteSynchronously意味着延续将使用与其前面的任务相同的线程运行。最后,您没有将任务组合到同一个方法中。传递给任务或延续的lambda是另一个匿名函数。您可以轻松地传递方法名,而不必使用lambda。如果你觉得第三方物流的运作方式不舒服,你可能应该寻找一个不同的库或模式,而不是试图与之对抗。这很可能就是答案。如果是这样的话,你会得到你的支持票并被接受。至于我的两点,我认为我们的交流已经中断了。1 ExecuteSynchronously意味着延续可能同时在多个线程上运行。在我的情况下,在任何给定的时间,延续必须只在一个线程上运行。2我知道这在技术上是它自己的方法,我也可以顺便调用一个方法。但这两件事实际上是在两个完全不同的类中,它们甚至不应该相互了解。这就是我的困境。有两件事,我不认为TaskContinuationOptions.Executes同步地做我需要的事情。在您的示例中,移动文件的操作不能在多个线程上同时发生。让我们假设它不是移动文件,而是通过串行电缆与设备通信。第二,就像我说的,它不仅仅是我简化的。我认为把这两项任务合并成一种方法是不合适的。但这迫使我绕过看起来像是坏模式的任务。你所说的坏模式是TPL的实际设计理念。在这个问题上,它实际上非常接近F。其次,ExecuteSynchronously意味着延续将使用与其前面的任务相同的线程运行。最后,您没有将任务组合到同一个方法中。传递给任务或延续的lambda是另一个匿名函数。您可以轻松地传递方法名,而不必使用lambda。如果你觉得第三方物流的运作方式不舒服,你可能应该寻找一个不同的库或模式,而不是试图与之对抗。这很可能就是答案。如果是这样的话,你会得到你的支持票并被接受。至于我的两点,我认为我们的交流已经中断了。1 ExecuteSynchronously意味着延续可能同时在多个线程上运行。在我的情况下,在任何给定的时间,延续必须只在一个线程上运行。2我知道这在技术上是它自己的方法,我也可以顺便调用一个方法。但这两件事实际上是在两个完全不同的类中,它们甚至不应该相互了解。这就是我的困境。