【问题标题】:How to avoid collection modification during JSON serialization in looped multithreaded task?如何避免循环多线程任务中 JSON 序列化期间的集合修改?
【发布时间】:2019-05-09 10:16:44
【问题描述】:

在使用Newtonsoft.Json 时,我在序列化为 JSON 文件时遇到问题。

在一个循环中,我在各个线程中执行任务:

List<Task> jockeysTasks = new List<Task>();

            for (int i = 1; i < 1100; i++)
            {
                int j = i;
                Task task = Task.Run(async () =>
                {
                    LoadedJockey jockey = new LoadedJockey();
                    jockey = await Task.Run(() => _scrapServices.ScrapSingleJockeyPL(j));

                    if (jockey.Name != null)
                    {
                        _allJockeys.Add(jockey);
                    }

                    UpdateStatusBar = j * 100 / 1100;

                    if (j % 100 == 0)
                    {
                        await Task.Run(() => _dataServices.SaveAllJockeys(_allJockeys)); //saves everything to JSON file
                    }
                });

                jockeysTasks.Add(task);
            }

            await Task.WhenAll(jockeysTasks);

还有if (j % 100 == 0),正在尝试将集合_allJockeys 保存到文件中(我会做一些计数器以使其更可靠,但这不是重点):

public void SaveAllJockeys(List<LoadedJockey> allJockeys)
        {
            if (allJockeys.Count != 0)
            {
                if (File.Exists(_jockeysFileName)) File.Delete(_jockeysFileName);

                try
                {
                    using (StreamWriter file = File.CreateText(_jockeysFileName))
                    {
                        JsonSerializer serializer = new JsonSerializer();
                        serializer.Serialize(file, allJockeys);
                    }
                }
                catch (Exception e)
                {
                    dialog.ShowDialog("Could not save the results, " + e.ToString(), "Error");
                }
            }
        }

据我所知,在那段时间里,另一个任务正在向集合中添加新的集合项,它向我抛出了异常:

集合已修改;枚举操作可能无法执行。

正如我在THE ARTICLE 中阅读的那样,您可以更改迭代类型以避免异常。据我所知,无法修改方式,Newtonsoft.Jsonpack 是怎么做的。

提前感谢您提供有关如何避免异常并保存集合而不发生意外更改的任何提示。

【问题讨论】:

    标签: c# json.net task jsonserializer


    【解决方案1】:

    您可能应该从 List 继承并使用 ReaderWriterLock (https://docs.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlock?view=netframework-4.8)

    即(未测试伪 C#)

    public class MyJockeys: List<LoadedJockey>
    {
    
        System.Threading.ReaderWriterLock _rw_lock = new System.Threading.ReaderWriterLock();
    
        public new Add(LoadedJockey j)
        {
           try
           {
               _rw_lock.AcquireWriterLock(5000); // or whatever you deem an acceptable timeout
              base.Add(j);
           }
           finally
           {
               _rw_lock.ReleaseWriterLock();
           }
        }
    
        public ToJSON()
        {
           try
           {
               _rw_lock.AcquireReaderLock(5000); // or whatever you deem an acceptable timeout
              string s = "";  // Serialize here using Newtonsoft
              return s;
           }
           finally
           {
               _rw_lock.ReleaseReaderLock();
           }
        }
        // And override Remove and anything else you need
    }
    

    明白了吗?

    希望这会有所帮助。

    问候,

    亚当。

    【讨论】:

    • 感谢您的帮助,但我找到了一个更简单的解决方案。我绑定在集合上使用ToList(),创建列表副本,具有积极作用。
    • 很高兴听到。很高兴你被排序:-)
    【解决方案2】:

    我绑定在集合上使用ToList(),创建列表的副本,具有积极作用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多