2

我正在使用BlockingCollection生产者消费者模式,我得到了一个例外,我想在上面写一个专利——谷歌只有两个结果!期望是“CompleteAdding 不能与添加到集合中同时使用”,它发生在 iTryAdd在 BlockingCollection 上,如下所示:

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

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

微软的人有想法吗?我应该在取消之后和调用 CompleteAdding 之前睡觉吗?

4

1 回答 1

5

看这段代码:

    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”被捕获,所有任务都使用相同的 var。除了前几个之外,其他所有人都会看到i == _workers.Length,因为它们是在 for 循环完成后执行的。

这是一个经典的 lambda + 捕获的 var 问题。

于 2010-10-14T20:19:00.863 回答