Strange appearance of a null entry in the list of

2019-05-23 02:52发布

Here is the code involved:

private static async Task DoRunInOrderAsync<TTaskSeed>(SemaphoreSlim sem, IObservable<TTaskSeed> taskSeedSource, CreateTaskDelegate<TTaskSeed> createTask, OnTaskErrorDelegate<TTaskSeed> onFailed, OnTaskSuccessDelegate<TTaskSeed> onSuccess) where TTaskSeed : class
{
    var tasks = await taskSeedSource
        .Select(taskSeed => GetPendingOrRunningTask(taskSeed, createTask, onFailed, onSuccess, sem))
        .ToList()
        .ToTask();

    await Task.WhenAll(tasks);
}
private static async Task GetPendingOrRunningTask<T>(T taskSeed, CreateTaskDelegate<T> createTask, OnTaskErrorDelegate<T> onFailed, OnTaskSuccessDelegate<T> onSuccess,
    SemaphoreSlim sem) where T : class
{
    Exception exc = null;
    await sem.WaitAsync();
    try
    {
        var task = createTask(taskSeed);
        if (task != null)
        {
            await task;
        }
        onSuccess(task, taskSeed);
    }
    catch (Exception e)
    {
        exc = e;
    }

    sem.Release();

    if (exc != null)
    {
        onFailed(exc, taskSeed);
    }
}

Where:

  • Select is IObservable<TResult> Select<TSource, TResult>(this IObservable<TSource> source, Func<TSource, TResult> selector) from System.Reactive.Linq.Observable
  • ToList is IObservable<IList<TSource>> ToList<TSource>(this IObservable<TSource> source) from System.Reactive.Linq.Observable
  • ToTask is Task<TResult> ToTask<TResult>(this IObservable<TResult> observable) from System.Reactive.Threading.Tasks.TaskObservableExtensions
  • System.Reactive.Linq version is 2.2.5.0

enter image description here

As far as I can see, everything is built, no stale binaries around. The error occurs often, but not always.

For the life of me, I cannot understand how the tasks list can contain null if the GetPendingOrRunningTask method is async Task ?

EDIT

enter image description here

So ToList injects null. How? Why? What am I doing wrong (besides programming for a living) ?

1条回答
forever°为你锁心
2楼-- · 2019-05-23 03:24

You may have a race condition.

.ToList() calls the List<T> constructor. The code is here.

If the number of elements in your taskSeedSource changes between the time the constructor starts and finishes, it's possible that you could end up with an inconsistent list. In particular look at line 94.

ICollection<T> c = collection as ICollection<T>;
if( c != null) {
    int count = c.Count;
    if (count == 0)
    {
        _items = _emptyArray;
    }
    else {
        _items = new T[count];
        c.CopyTo(_items, 0);  /* it's possible there are now fewer elements in c */
        _size = count;
    }
} 
查看更多
登录 后发表回答