2016-09-28 51 views
2

我有一个foreach循环,创建这样的多任务:如何处理它在任务中创建的对象?

[编辑:CreateDisposableAsync返回任务[IDisposable的]

foreach(...) 
{ 
    tasks.Add(CreateDisposableAsync()); 
} 

,后来我在等待着所有的这些任务,并捕获任何异常:

try 
{ 
    await Task.WhenAll(tasks); 
} 
catch (AggregateException) 
{ 
    // handle exceptions 
} 

但调用CreateDisposableAsync()返回一个IDisposable的,这是我想被布置是否有在任何任务异常。我怎样才能做到这一点?

[编辑:原来,在CreateDisposableAsync()函数处置其创建的对象,如果本身抛出异常,所以没有什么错的原始代码]

+0

'tasks'的类型是什么?你确定你需要处置吗? https://blogs.msdn.microsoft.com/pfxteam/2012/03/25/do-i-need-to-dispose-of-tasks/ –

+0

最简单的方法是确保'CreateDisposableAsync'清理自己的资源所以它负责处理它创建的需要处理的任何对象。 – Igor

+0

@ LasseV.Karlsen返回类型是任务

回答

2

从评论

问:与放置,结果你做(或者希望做的)任何其他与结果调用代码

A的异常:无我不需要

最简单的方法是让CreateDisposableAsync方法在返回之前清理自己的资源并返回Task而不是Task<IDisposable>。 OP中显示的现有呼叫代码不必更改。

// change the signature 
async Task CreateDisposableAsync(){ 
    // use using blocks for anything that needs to be disposed 
    // try/finally is also acceptable 
    using(var someDisposableInstance = new SomethingDisposable()){ 
     // implementation 
    } 
} 
1

只有运行完成任务将已经返回可能被丢弃的物体。只需将列表筛选到完成的任务,然后选择结果即可。

try 
{ 
    try 
    { 
     await Task.WhenAll(tasks); 
    } 
    catch (AggregateException) 
    { 
     // handle exceptions 
    } 

    //do other stuff with the returned task objects. 
} 
finally 
{  
    foreach(var item in tasks.Where(x=>x.Status == TaskStatus.RanToCompletion).Select(x=>x.Result)) 
    { 
     //We use a try block so if Dispose throws it does not break the loop. 
     try 
     { 
      item.Dispose(); 
     } 
     catch(Exception ex) 
     { 
      //Log any exception on dispose. 
     } 
    } 
} 

或者,如果你不抛出错误不会返回对象WaitAll

try 
{ 
    await Task.WhenAll(tasks); 
} 
catch (AggregateException) 
{ 
    // handle exceptions 
} 
finally 
{  
    foreach(var item in tasks.Where(x=>x.Status == TaskStatus.RanToCompletion).Select(x=>x.Result)) 
    { 
     //We use a try block so if Dispose throws it does not break the loop. 
     try 
     { 
      item.Dispose(); 
     } 
     catch(Exception ex) 
     { 
      //Log any exception on dispose. 
     } 
    } 
} 

任务后,做其他任何工作计划,没有办法的CreateDisposableAsync()外弃掉,并如果出现任何错误,那么这个功能就有责任处理它们。

public async Task<MyDisposeableClass> CreateDisposableAsync() 
{ 
    MyDisposeableClass myDisposeableClass = null; 
    try 
    { 
     myDisposeableClass = new MyDisposeableClass(); 

     //... 

     return myDisposeableClass; 
    } 
    catch 
    { 
     //dispose of the class if the instance was created. 
     if(myDisposeableClass != null) 
      myDisposeableClass.Dispose(); 

     //let the execption bubble up. 
     throw; 
    } 
} 
+0

@Servy好点,固定。 –

+1

我不知道OP是否想要在catch(AggregateException)之后完成额外的工作,但在对象被抛弃之前。如果在'//用返回的任务对象做其他事情时没有任何事情要做',那么Servy提到两个try块可以合并,并且可以使用try/catch/finally。 –

+0

@Servy否,因为'AggregateException'会将你踢出try块,所以如果你想用成功完成的结果做一些事情,它需要在catch(AggregateException)块之后。编辑:但是,[在阅读OP的评论](http://stackoverflow.com/questions/39750215/how-to-dispose-of-an-object-that-it-created-in-a-task/39750333? noredirect = 1#comment66797413_39750215)我看到他没有做任何额外的工作。所以它可以合并。我会更新我的答案。 –

1

我张贴这在这里,因为它似乎是一个替代的解决方案:

private static async Task CallCreateDisposableAsync() 
    { 
     using (await CreateDisposableAsync()) { } 
    } 

然后

foreach(...) 
{ 
    tasks.Add(CallCreateDisposableAsync); 
} 

这样using语句可配置所创建的IDisposable。

+0

纠正我,如果我错了,但如果'CreateDisposableAsync'抛出一个异常,虽然它仍然不会被处置,因为它永远不会正式进入'使用'块。这意味着你仍然有同样的问题,如果你不能改变你的电话签名,Scott的答案将是最好的选择。 – Igor

+1

@Igor如果它抛出一个异常,那么永远不会有一个暴露的'IDisposable'对象可以被处理掉,所以不需要做任何事情(从外部调用者的角度来看,内部任何方法都会返回一次性资源应该有一个try/catch,并在资源创建后但返回之前可能抛出异常的情况下处理该资源。 – Servy

+0

@Servy - 如果假设这个方法创建了Disposable实例成员,那么这不会取决于CreateDisposableAsync中抛出异常的位置吗?我根据OP提到IDIsposable结果应该始终处理的事实做出了可能的假设,即使在发生异常情况时也是如此。所以是的,假设'CreateDisposableAsync'清除了如果有异常没有问题。 – Igor

相关问题