如何处置在任务中创建的对象

本文关键字:创建 对象 任务 何处置 | 更新日期: 2023-09-27 18:09:30

我有一个foreach循环,它创建了多个任务,像这样:

[edit: CreateDisposableAsync return a Task[IDisposable]]

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

,然后等待所有这些任务,并捕获任何异常:

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

但是对CreateDisposableAsync()的调用返回一个IDisposable,无论在任何任务中是否存在异常,我都希望处置它。我该怎么做呢?

[编辑:事实证明,CreateDisposableAsync()函数是处置其创建的对象,如果本身抛出一个异常,所以没有任何错误与原始代码]

如何处置在任务中创建的对象

从注释

Q:在调用代码

中,除了处理结果之外,你还对结果做(或想做)任何其他事情吗?

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
   }
}

只有运行到完成的任务才会返回可以被处置的对象。只需将列表筛选到已完成的任务,然后选择结果。

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;
    }
}

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

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

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