1

如何确定是否已等待任务/检查其结果?

我有一个创建和激活List的背景Task,但由于异常行为可能没有等待。在我的“finally”块中,我需要遍历此列表和await那些尚未等待的任务,以防止在 GC 时引发它们的潜在异常。

编辑

我的解决方案:

首先,感谢 Steve 澄清了我对如何在 .NET 4.5 中处理未观察到的异常的过时理解。这很有趣。但是,我仍然对从调用方法中引发这些异常感兴趣,因此我提出了以下简单的辅助类来提供对Task不立即观察到的 s 的轻松管理:

/// <summary>
/// Collection of unobserved tasks.
/// </summary>
public class TaskCollection
{
    /// <summary>
    /// The tasks.
    /// </summary>
    private readonly List<Task> tasks = new List<Task>();

    /// <summary>
    /// Gets a value indicating whether this instance is empty.
    /// </summary>
    /// <value>
    ///   <c>true</c> if this instance is empty; otherwise, <c>false</c>.
    /// </value>
    public bool IsEmpty
    {
        get
        {
            return this.tasks.Count == 0;
        }
    }

    /// <summary>
    /// Adds the specified task.
    /// </summary>
    /// <param name="task">The task.</param>
    /// <returns>The <see cref="task"/>.</returns>
    public Task Add(Task task)
    {
        Contract.Requires(task != null);
        Contract.Requires(!this.Contains(task));

        Contract.Ensures(this.Contains(task));
        Contract.Ensures(Contract.Result<Task>() == task);

        this.tasks.Add(task);
        return task;
    }

    /// <summary>
    /// Adds the specified task.
    /// </summary>
    /// <typeparam name="T">Task return type.</typeparam>
    /// <param name="task">The task.</param>
    /// <returns>The <see cref="task"/>.</returns>
    public Task<T> Add<T>(Task<T> task)
    {
        Contract.Requires(task != null);
        Contract.Requires(!this.Contains(task));

        Contract.Ensures(this.Contains(task));
        Contract.Ensures(Contract.Result<Task<T>>() == task);

        this.tasks.Add(task);
        return task;
    }

    /// <summary>
    /// Determines whether [contains] [the specified task].
    /// </summary>
    /// <param name="task">The task.</param>
    /// <returns>
    /// <c>true</c> if [contains] [the specified task]; otherwise, <c>false</c>.
    /// </returns>
    public bool Contains(Task task)
    {
        Contract.Requires(task != null);

        return this.tasks.Contains(task);
    }

    /// <summary>
    /// Observes the specified task.
    /// </summary>
    /// <param name="task">The task.</param>
    /// <returns>
    /// The task.
    /// </returns>
    public Task When(Task task)
    {
        Contract.Requires(task != null);
        Contract.Requires(this.Contains(task));

        Contract.Ensures(!this.Contains(task));

        this.tasks.Remove(task);
        return task;
    }

    /// <summary>
    /// Observes the specified task.
    /// </summary>
    /// <typeparam name="T">Return type.</typeparam>
    /// <param name="task">The task.</param>
    /// <returns>
    /// The task.
    /// </returns>
    public Task<T> When<T>(Task<T> task)
    {
        Contract.Requires(task != null);
        Contract.Requires(this.Contains(task));

        Contract.Ensures(!this.Contains(task));

        this.tasks.Remove(task);
        return task;
    }

    /// <summary>
    /// Observes all tasks.
    /// </summary>
    /// <returns>The task.</returns>
    public Task WhenAll()
    {
        Contract.Ensures(this.IsEmpty);

        if (this.IsEmpty)
        {
            return TaskEx.FromResult<object>(null);
        }

        var taskArray = this.tasks.ToArray();
        this.tasks.Clear();

        return TaskEx.WhenAll(taskArray);
    }
}

示例用法:

Exception exception;
var tasks = new TaskCollection();
try
{
    var backgroundTask = tasks.Add(DoSomethingAsync());
    // Do something in parallel.
    await tasks.When(backgroundTask);
}
catch (Exception ex)
{
    if (tasks.IsEmpty)
    {
        throw;
    }

    exception = ex;
}

try
{
    await tasks.WhenAll();
}
catch (Exception ex)
{
    exception = new AggregateException(ex, exception);
}

throw new AggregateException(exception);
4

2 回答 2

5

在我的“finally”块中,我需要遍历此列表并等待那些尚未等待的任务,以防止在 GC 时引发它们的潜在异常。

不,你没有。

在 .NET 4.5 中,在 finalization 期间不再引发未观察到的任务异常

于 2013-05-08T11:37:30.320 回答
2

您需要在这里小心……一旦事情开始恶化,就很难保证该任务将永远完成(以任何方式)。但是 - 您可以添加如下方法:

public static void IgnoreFailure(this Task task) {
    if (task.IsCompleted) { // observe now
        // this is just an opaque method to ensure it gets evaluated
        if (task.IsFaulted) GC.KeepAlive(task.Exception);
    }
    else { // observe in the future
        task.ContinueWith(t => {
            if (t.IsFaulted) GC.KeepAlive(t.Exception);
        }, TaskContinuationOptions.ExecuteSynchronously);
    }
}

只需调用t.IgnoreFailure()每个任务,无论它是否已被等待。

于 2013-05-08T09:55:55.697 回答