【问题标题】:Exception when attempting to modify Dictionary尝试修改字典时出现异常
【发布时间】:2015-08-29 15:38:07
【问题描述】:

我正在创建一个字典来管理线程,当我在字典中添加一些线程后尝试清除死线程时遇到了这个异常。

using System;
using System.Collections.Generic;
using System.Threading;

namespace SysCat {
    public class ThreadManager {
        public static readonly ThreadManager GlobalThreadManager = new ThreadManager( );

        /// <summary>
        /// Initializes a new instance of the <see cref="SysCat.ThreadManager"/> class.
        /// </summary>
        public ThreadManager( ) {

        }

        /// <summary>
        /// Create a new ThreadManager with all the Threads within baseTM and deinitializes baseTM
        /// </summary>
        public ThreadManager( ThreadManager baseTM ) {
            this.threads = baseTM.threads;
            baseTM.threads.Clear( );
        }

        private Dictionary<Guid,Thread> threads = new Dictionary<Guid, Thread>( );

        /// <summary>
        /// Attempts to obtain an unused ThreadID within an attempt limit
        /// </summary>
        /// <returns>The unused ThreadID, if found</returns>
        /// <param name="attempts">The number of iterations to try.</param>
        public Guid getUnusedThreadID( int attempts ) {
            lock( threads ) {
                Guid threadID;
                int totalAttempts = attempts;
                while( threads.ContainsKey( ( threadID = Guid.NewGuid( ) ) ) ) {
                    attempts--;
                    if( attempts == 0 )
                        throw new NoOpenThreadIDException( totalAttempts );
                }
                return threadID;
            }
        }

        /// <summary>
        /// Attempts to get a Thread via its ThreadID, if it exists
        /// </summary>
        /// <returns>The Thread</returns>
        /// <param name="threadID">The ThreadID to use</param>
        public Thread getThread( Guid threadID ) {
            lock( threads ) {
                Thread tryGet;
                if( !threads.TryGetValue( threadID, out tryGet ) )
                    throw new ArgumentException( "Specified ThreadID does not exist in this ThreadManager" );
                return tryGet;
            }
        }

        /// <summary>
        /// Attempts to get a ThreadID via the Thread
        /// </summary>
        /// <returns>The ThreadID</returns>
        /// <param name="thread">The Thread to use</param>
        public Guid getThreadID( Thread thread ) {
            lock( threads ) {
                if( threads.ContainsValue( thread ) ) {
                    foreach( Guid id in threads.Keys ) {
                        Thread t;
                        threads.TryGetValue( id, out t );
                        if( t.Equals( thread ) )
                            return id;
                    }
                    // I should never get here
                    return Guid.Empty;
                }
                else
                    throw new ArgumentException( "Specified Thread does not exist in this ThreadManager" );
            }
        }

        /// <summary>
        /// Adds the Thread, unless it cannot find an open ThreadID
        /// </summary>
        /// <returns>The ThreadID used to register the Thread</returns>
        /// <param name="thread">The Thread to register</param>
        public Guid addThread( Thread thread ) {
            lock( threads ) {
                Guid id;
                try {
                    id = getUnusedThreadID( 500 );
                }
                catch( NoOpenThreadIDException e ) {
                    throw e;
                }
                threads.Add( id, thread );
                return id;
            }
        }

        /// <summary>
        /// Attempts to clear all stopped and null Threads
        /// </summary>
        /// <returns>The number of dead Threads cleared</returns>
        public int clearDeadThreads() {
            int cleared = 0;
            lock(threads) {
                foreach( Guid id in threads.Keys ) {
                    Thread thread;
                    threads.TryGetValue( id, out thread );
                    if( thread == null ) {
                        // Does not exist, Thread is dead
                        cleared++;
                        threads.Remove( id );
                        continue;
                    }
                    if( !thread.IsAlive ) {
                        // Not alive, Thread is dead
                        cleared++;
                        threads.Remove( id );
                        continue;
                    }
                }
            }

            return cleared;
        }

        public class NoOpenThreadIDException : Exception {
            public NoOpenThreadIDException( int attempts )
                : base( string.Format( "Unable to find an open ThreadID within the attempt limit of {0}", attempts ) ) {
            }
        }
    }
}

我得到了这个异常:System.InvalidOperationException 带有消息:集合已修改;枚举操作可能无法执行

我不确定为什么会这样,任何帮助将不胜感激。

这是我用来测试的代码:

using System;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.Threading;

using _TM = SysCat.ThreadManager;

namespace SysCat {
    public class SysCat {
        Thread T = new Thread( new ThreadStart( delegate {
            while(true) Thread.Sleep(250);
        } ) );
        Thread T2 = new Thread( new ThreadStart( delegate {

        } ) );
        Thread T3 = new Thread( new ThreadStart( delegate {

        } ) );
        _TM.GlobalThreadManager.addThread( T );
        _TM.GlobalThreadManager.addThread( T2 );
        _TM.GlobalThreadManager.addThread( T3 );
            Console.WriteLine( _TM.GlobalThreadManager.clearDeadThreads( ) );
            Console.ReadLine( );
        }
    }
}

【问题讨论】:

    标签: c# .net exception dictionary


    【解决方案1】:

    在枚举IEnumerable 时,您不能修改它。

    您可以做一个简单的修改并获取您正在枚举的内容的副本,以便对其进行更改不会导致System.InvalidOperationException

    例如代替

    foreach( Guid id in threads.Keys) 
    

    使用

    foreach( Guid id in threads.Keys.ToList()) 
    

    请记住,您可以使用data structures that supports concurrency

    【讨论】:

    • 我正在使用锁来同步代码。但是,感谢您向我指出并发部分。
    • 我没有在我的方法列表中看到它,它只是在特定版本的 .net 中吗?
    • 尝试添加using System.Linq
    • 感谢您的帮助!让我让接受倒计时结束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-30
    • 2021-09-01
    • 2017-07-08
    • 2018-10-26
    相关资源
    最近更新 更多