очень уникальное исключение для BlockingCollection на .net 4.0

я использую BlockingCollection для шаблона Producer Consumer, и у меня есть исключение, я думаю написать на него патент - только два результата в google! ожидается, что «CompleteAdding не может использоваться одновременно с добавлениями в коллекцию», и это происходит, когда я TryAdd в th BlockingCollection следующим образом:

 public void EnqueueTask(T item)
    {
        if (!_cancellationTokenSource.IsCancellationRequested)
        {
            _workerQueue.Add(item);
        }
    }

CompleteAdding вызывается для класса-оболочки Consumer-Producer:

  public void Dispose()
    {
        if (!_IsActive)
            return;
        _IsActive = false;
        _cancellationTokenSource.Cancel();
        _workerQueue.CompleteAdding();
        // Wait for the consumer's thread to finish.
        for (int i = 0; i < _workers.Length; ++i)
        {
            Task t1 = Task.Factory.StartNew(() =>
            {
                try
                {
                    if (!_workers[i].Join(4000))
                        LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
                }
                catch (Exception ex)
                {
                    OnLogged(ex.Message + ex.StackTrace);
                }
            });

        }


        // Release any OS resources.
    }

У кого-нибудь из Microsoft есть идея? я должен спать после отмены и до вызова CompleteAdding?


person user437631    schedule 14.10.2010    source источник
comment
Каков тип исключения? В документации сказано, что AddTryAdd) выдаст InvalidOperationException, если вы попытаетесь добавить элемент после вызова CompleteAdding. Ваше описание не слишком ясно, но если вы получаете исключение при вызове Add, я подозреваю, что это так.   -  person Jim Mischel    schedule 31.07.2013


Ответы (1)


Посмотрите на этот кусок кода:

    for (int i = 0; i < _workers.Length; ++i)
    {
        Task t1 = Task.Factory.StartNew(() =>
        {
            try
            {
                if (!_workers[i].Join(4000))   << == Here
                    LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
            }

В _workers[i].Join(4000) значение i не то, что вы думаете. Попробуйте еще раз:

   for (int i = 0; i < _workers.Length; ++i)
    {
        int j = i;  // copy
        Task t1 = Task.Factory.StartNew(() =>
        {
            try
            {
                if (!_workers[j].Join(4000))  // j
                    LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
            }

В вашей версии захвачена переменная «i», и все задачи используют одну и ту же переменную. Все, кроме первых нескольких, увидят i == _workers.Length, потому что они выполняются после завершения цикла for.

Это классическая проблема лямбда + захваченная переменная.

person Henk Holterman    schedule 14.10.2010
comment
А, классическая захваченная переменная итерации. Хорошее место. - person Marc Gravell; 15.10.2010
comment
это не относится к вопросу, но если вы имеете в виду ++ i вместо i ++ - это опечатка. - person user437631; 15.10.2010
comment
@user — сделать копию i внутри foreach; ссылаться только на эту копию. - person Marc Gravell; 15.10.2010
comment
@user, нет, дело не в i++, а в использовании i внутри лямбды. - person Henk Holterman; 15.10.2010