Как избежать модификации коллекции во время сериализации JSON в циклической многопоточной задаче? - PullRequest
1 голос
/ 09 мая 2019

У меня проблема при сериализации в файл JSON, при использовании Newtonsoft.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");
                }
            }
        }

В это время, как я полагаю,другие задачи - добавление нового элемента коллекции в коллекцию, и это вызывает у меня исключение:

Коллекция была изменена;Операция перечисления может не выполняться.

Как я читал в СТАТЬЯ , вы можете изменить тип итерации, чтобы избежать исключения.Насколько я знаю, я не могу изменить способ, как Newtonsoft.Json pack делает это.

Заранее благодарю за любые советы, как избежать исключения и сохранить коллекцию без неожиданных изменений.

Ответы [ 2 ]

2 голосов
/ 09 мая 2019

Вы, вероятно, должны наследовать от 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
}

Получите идею?

Надеюсь, это поможет.

С уважением,

Адам.

0 голосов
/ 06 июля 2019

Я решил использовать ToList() для коллекции, что создает копию списка, с положительным эффектом.

...