我发现自己养成了习惯,即把Task
对象保存为超出结果容器的完成范围。
到目前为止,我还没有发现任何弊端,而且我发现代码比完成任务后使用单独的变量存储结果更清晰。
以下是一些用法示例。尽管我认为这并不重要,但它们已作为MVVM应用程序中的视图模型的一部分。(请注意,这不是实际的工作代码,我仅尝试概述模式。)
早期初始化
ASlowClient
是一类需要几秒钟才能连接到WCF或REST服务的类。因此,我将通过任务尽快对其进行初始化。当需要客户端时,将等待任务,并产生初始化的消息SlowClient
(如果任务完成,则立即进行初始化,或者等待任务完成之后)。
所以我会做这样的事情:
public class SomeViewModel
{
private Task<SlowClient> _slowClientInitializerTask;
public SomeViewModel()
{
_slowClientInitializerTask = Task.Run(() => CreateAndInitSlowClient());
}
private SlowClient CreateAndInitSlowClient()
{
// SlowClient instantiation and initialization, taking a few seconds...
}
// Task result consumer example
void OnSomeCommandExecuted(object parameter)
{
try
{
var client = await _slowClientInitializerTask;
// do something with the client
} catch {
// may re-create the task if the client ceases to be valid
}
}
}
如所示OnSomeCommandExecuted
,每个使用的方法SlowClient
都将简单地执行以下操作:
var client = await _slowClientInitializerTask;
如果由于某种原因结果不再有效(与服务的连接被删除或无效),我将运行一个新任务并将其分配给_slowClientInitializerTask
-就像构造函数中显示的代码一样。
这种模式的替代方法是创建一些额外的_slowClient
变量,一旦任务完成,该变量就会更新,因此每次使用它时都需要进行检查。例如:
if (_slowClient == null)
_slowClient = await _slowClientInitializerTask;
我看不出任何好处,只会增加复杂性。
后台工作者
一个更复杂的示例使用任务来处理图像,创建一个包含调整大小图像的新文件。必须生成包含这些图像的报告;它通过它们的路径访问图像文件,并且在任何可能的情况下都必须使用调整大小的版本-如果不是,则使用原始图像。因此,我需要能够将原始图像的路径映射到其调整大小的版本。
// Key: original image path; Value: task returning the re-sized image path
private Dictionary<string, Task<string>> _resizeTasks;
// Costly operation => Want to execute it asynchronously
private string ResizeImage(string originalImagePath)
{
// returns the path of a temporary resized image file
}
// Command execution handler for instance => Launches image resize on background
void OnAddImageExecuted(object parameter)
{
string path = parameter as string;
if (!_resizeTasks.Keys.Contains(path))
_resizeTasks[path] = Task.Run(() => ResizeImage(path));
}
// Generates a report consuming the images => Requires the result of the tasks
void OnGenerateReportExecuted(object parameter)
{
try {
foreach (var faulted in from entry in _resizeTasks
where entry.Value.IsFaulted select entry.Key)
_resizeTasks[path] = Task.Run(() => ResizeImage(path)); // Retry
await Task.WhenAll(_resizeTasks.Values); // Wait for completion
} catch { } // Ignore exceptions thrown by tasks (such as I/O exceptions)
var imagePaths = _resizeTasks[path].Select(entry =>
entry.Value.Status == TaskStatus.RanToCompletion ?
entry.Value.Result : entry.Key);
// generate the report requiring the image paths
}
ConcurrentDictionary
由于对图像的添加是异步执行的,因此实际的实现方式使用。此外,可以删除图像并再次添加图像,因此对于当前添加的图像会有单独的列表,并且_resizeTasks
还可以用作先前调整大小的图像的缓存。
任务处理是不是话题在这里,因为我以后可以和处置反正他们,似乎在这种情况下没有必要,因为在陈述中,我需要处理的任务?从张贴在.NET并行编程MSDN博客:
不会。不要烦恼处理任务,除非性能或可伸缩性测试表明您需要根据使用模式对它们进行处理才能达到性能目标。如果确实需要处理它们,请仅在简单的情况下进行处理,即当您已经在代码中有一点可以100%确保它们已完成并且没有其他人在使用时他们。
我的担忧如下:
*另一个缺点是,如果您await
多次执行错误的任务,则每次都会引发异常。这可能会出现问题,但这取决于您的特定异常处理。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句