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

时间:2017-03-27 08:50:08

标签: c# .net arrays multithreading dictionary

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

  

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

我认为这是因为使用了字典,所以我将其切换为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 []   数组,Int32索引)at   System.Collections.Generic.Dictionary`2.GetObjectData(的SerializationInfo   info,StreamingContext context)at   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(对象   obj,ISurrogateSelector surrogateSelector,StreamingContext context,   SerObjectInfoInit serObjectInfoInit,IFormatterConverter转换器,   ObjectWriter objectWriter,SerializationBinder binder)at   System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo   objectInfo,NameInfo memberNameInfo,NameInfo typeNameInfo)at   System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArrayMember(WriteObjectInfo   objectInfo,NameInfo arrayElemTypeNameInfo,Object data)at   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)at   System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(对象   graph,Header [] inHeaders,__BinaryWerer serWriter,Boolean fCheck)
  在   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(流   serializationStream,Object graph,Header [] headers,Boolean fCheck)
  在AggregateRunner.Enteties.Saver.SaverCallback()

2 个答案:

答案 0 :(得分:0)

为避免此错误,我在尝试序列化文件之前使用lock。 现在它正常工作。

答案 1 :(得分:0)

据我所知,您每小时都会为您的队列拍摄一张快照,但代码的主要问题是您正在尝试序列化您的队列({{{{ 1}},我猜)本身没有任何克隆或同步逻辑。您的代码的另一个问题是ConcurrentQueue<T>用法,这里根本不需要。

您面临的例外情况是goto,是ArgumentException的{​​{1}} CopyTo方法。在序列化期间发生了这种情况,因为某些线程将一些消息添加到队列中,现在它不适合决定使用的阵列序列化器。

因此,在这种情况下你可以做的是在访问原始队列期间引入一些锁定(你决定这样做),或者为你的队列创建一个克隆并以安全的方式序列化阻止其他线程。

您可以自己使用ConcurrentQueue<T> ConcurrentQueue<T>方法执行此操作,创建长度大于队列中消息数的数组,但锁定通常是克隆数据的更方便的方法。所以你的代码应该是这样的:

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();
}