在完成任务对象很长时间之后是否还有其他缺点?

再次

我发现自己养成了习惯,即把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%确保它们已完成并且没有其他人在使用时他们。

我的担忧如下:

  1. 这是一个好的用法吗?
  2. 是否有任何缺点或陷阱?(例如,内存泄漏,潜在的死锁,锁定的池资源等)
  3. 相反,我是否应该解开任务的结果并直接存储它们(因此,我认为这会增加代码复杂性)?
i3arnon
  1. 如果它为您带来了更清晰的代码,那么是的,这是一个很好的模式。
  2. 唯一真正的潜在缺点是这些任务不会被垃圾收集器收集。只要您使用这种模式几次,而不会成千上万次,这对您来说永远不会是一个问题*(不必担心死锁或线程池资源,因为完成的任务就是这样完成的。)永远不会有另一个线程来处理它,因为任务是不可重用的
  3. 只要内存不成问题,就没有理由这样做。

*另一个缺点是,如果您await多次执行错误的任务,则每次都会引发异常。这可能会出现问题,但这取决于您的特定异常处理。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

带有Gradle的Spring Boot无法完成任务:test?

Codemagic IOS构建需要很长时间才能完成

如何分析在SBT中完成任务所花费的时间

在Task.WhenAll中完成任务时是否有回调

Spark程序需要很长时间才能完成执行

AWS Glue需要很长时间才能完成

页面不会等待其他页面完成任务再继续

如何检查ffmpeg完成任务的时间?

MySQL-检查完成的约会后,患者是否还有其他约会,按天细分

异步任务进度对话框需要很长时间才能在Android中完成简单任务

如何识别带有任务的Python线程是否完成任务?

为什么此查询需要很长时间才能完成?

在吉拉完成任务的平均时间

Java:尽管执行非常简单的任务,线程仍需要很长时间才能完成

颤动计数时间以在后台完成任务

我简单的oracle查询需要很长时间才能完成

如何检查堆栈上是否还有其他活动?

带有参数的Jbpm完成任务-地图为空

SQL连接查询需要很长时间才能完成

是否还有其他像Common Lisp一样在时间上冻结的有趣语言?

OpenShift oc rsync需要很长时间才能完成

我的while循环是有缺陷还是需要很长时间才能完成?

sql 查询需要很长时间才能完成

R 中的循环需要很长时间才能完成

Javascript Async / Await 完成任务并最终做其他事情

程序没有完成任务就结束了

带有 Promises.all 的 Firestore 功能需要很长时间才能完成

我的程序需要很长时间才能完成这些 if 语句

Spring Batch 需要很长时间才能完成工作