目标数组长度不足,无法复制集合中的所有项目。请检查数组索引和长度。

4

我有这段代码,它给了我一个错误:

目标数组的长度不足以复制集合中的所有项。请检查数组索引和长度。

我认为这是因为使用了字典,所以我将其切换到 ConcurrentDictionary,但错误仍然存在。

private void SaverCallback()
{
    AddThread("Main Thread");
    const string path = "milestone";
    while (!stop)
    {
        ConcurrentDictionary<string, object> milestone = new ConcurrentDictionary<string, object>();
        milestone.TryAdd("Jobs", JobQueue.Queue.MainQueue);
        milestone.TryAdd("Locked Jobs", JobQueue.Queue.LockedQueue);

    again: try {
            using (FileStream writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
            {
                BinaryFormatter formater = new BinaryFormatter();
                formater.Serialize(writingStream, milestone);
                writingStream.Flush();
                Logger.Debug("Status saved");
            }
        }
        catch(Exception e)
        {
            Logger.Error($"Milestone exception: {e.Message}");
            goto again;
        }
        this.WaitTime(60000);
    }
    RemoveThread();
}

UPD:

目标数组长度不足以复制集合中的所有项。请检查数组索引和长度。并且在System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)处、在System.Collections.Generic.Dictionary`2.CopyTo(KeyValuePair`2[] array, Int32 index)处、在System.Collections.Generic.Dictionary`2.GetObjectData(SerializationInfo info, StreamingContext context)处抛出。在System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)处,在System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)处,在System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArrayMember(WriteObjectInfo objectInfo, NameInfo arrayElemTypeNameInfo, Object data)处,在System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArray(WriteObjectInfo objectInfo, NameInfo memberNameInfo, WriteObjectInfo memberObjectInfo)处,在System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)处抛出。在System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)处,AggregateRunner.Enteties.Saver.SaverCallback()。


6
不要使用 goto,它表示主要的设计问题。唯一允许跳转的语言是IL和ASM。 - Sebastian L
JobQueue.Queue.MainQueueJobQueue.Queue.LockedQueue的类型是什么? - Matthew Watson
@MatthewWatson 一些线程池的实现 - Ekaterina
那么,在将milestone序列化的过程中,另一个线程是否可以向作业队列添加项目?如果是这样,可能在该过程中会发生一些不良情况。 - Matthew Watson
2
所以,如果出现异常,记录下来并使用goto无限重试。这听起来不好吧? - Gian Paolo
显示剩余2条评论
2个回答

3
据我所见,您每隔一个小时对您的队列进行一次快照,但是您代码的主要问题在于您尝试将队列(我猜是ConcurrentQueue)本身序列化而没有进行任何克隆或同步逻辑。您代码的另一个问题是使用了goto语句,在这里完全没有必要。
您面临的异常是ArgumentException,并且是ConcurrentQueue的CopyTo方法的第三种可能异常。由于某个线程添加了一些消息到队列中,现在它不适合于序列化器决定使用的数组,因此会发生这种情况。
因此,在这种情况下,您可以采取两种措施之一:引入一些锁定以在访问原始队列时保持安全(这是您决定要做的),或者创建原始队列的克隆并以安全的方式对其进行序列化,而不会阻塞其他线程。
您可以使用ConcurrentQueue<T>CopyTo方法自行完成此操作,通过创建长度大于队列中消息数的数组,但是一般情况下锁定更方便地克隆数据。因此,您的代码应该像这样:
private void SaverCallback()
{
    AddThread("Main Thread");
    const string path = "milestone";
    while (!stop)
    {
        try
        {
            lock (JobQueue.Queue.MainQueue)
            lock (JobQueue.Queue.LockedQueue)
            {
                using (var writingStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
                {
                    var milestone = new ConcurrentDictionary<string, object>();
                    milestone.TryAdd("Jobs", JobQueue.Queue.MainQueue);
                    milestone.TryAdd("Locked Jobs", JobQueue.Queue.LockedQueue);

                    var formater = new BinaryFormatter();
                    formater.Serialize(writingStream, milestone);
                    writingStream.Flush();
                    Logger.Debug("Status saved");
                }
            }
            // this line cloud be in finally case too,
            // if you don't need to save a queue in case of some errors
            this.WaitTime(60000);
        }
        catch(Exception e)
        {
            // note that logger accepts whole exception information
            // instead of only it's message
            Logger.Error($"Milestone exception: {e}");
            continue;
        }
    }
    RemoveThread();
}

0
为了避免这个错误,在尝试对文件进行序列化之前,我使用锁。 现在它可以正常工作了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接