C #, переданный объект состояния в задачу, изменяется

#c# #multithreading #task #task-parallel-library

#c# #многопоточность #задача #задача-параллельная-библиотека

Вопрос:

Я борюсь с этим несколько дней, надеюсь, вы сможете подтолкнуть меня в правильном направлении.

Это рекурсивный алгоритм обработки потоков, который анализирует ресурс в потоке в поисках ссылок на другие ресурсы, сохраняя их в ConcurrentBag для будущих извлечений. Создание потоков ограничено массивом с настраиваемым размером для сохранения ресурсов.

У меня есть private static ConcurrentBag<string> , который заполняется многими потоками. Это те, Tasks которые хранятся в private static Task[] с настраиваемым размером (настройки приложения).
Существует цикл, в Main котором выполняется TryTake() преобразование в локальную string url переменную. При успешном выполнении он выполняет цикл, Task[] пытаясь найти пустой слот, создавая новый объект состояния Task передачи url и сохраняя его в Task[] таком:

 TaskArray[x] = new Task(FindLinks, url, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
  

FindLinks Объявляется как

 private static readonly Action<object> FindLinks = input => { ... }
  

В основном Task[] цикле я устанавливаю url значение null перед следующим TryTake(out url) .

Моя проблема здесь в том, что объект состояния, input который передается из url основного цикла, становится null внутри лямбда-функции задачи. Я прочитал почти все статьи MSDN о TPL и не могу разобраться в этой : (

Как я могу передать переменную (строку) в Task безопасно без закрытия (или что бы это ни происходило).

Любые другие идеи по улучшению этого алгоритма также приветствуются.

Спасибо.

Редактировать:
Я решил проблему, изменив порядок инструкций и слегка переписав код в основном цикле. Переменной больше не присваивается значение null. Я подозреваю, что это было вызвано переупорядочением или вытеснением инструкции компилятора. Вот как это выглядит сейчас, не вызывая больше проблем:

 string url;
if (CollectedLinks.TryTake(out url))
{
  var queued = false;
  while (!queued)
  {
    // Loops thru the array looking for empty slot (null)
    for (byte i = 0; i < TaskArray.Length; i  )
    {
      if (TaskArray[i] == null)
      {
        TaskArray[i] = new Task(FindLinks, url, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
        TaskArray[i].Start(TaskScheduler.Current);
        queued = true; break;
      }
    }

    if (!queued)
    {
      // Loop and clean the array
      for (var i = 0; i < TaskArray.Length; i  )
      {
        if (TaskArray[i] == null)
          continue;
        if (TaskArray[i].Status == TaskStatus.RanToCompletion || TaskArray[i].Status == TaskStatus.Canceled || TaskArray[i].Status == TaskStatus.Faulted)
        {
          TaskArray[i].Wait(0);
          TaskArray[i] = null;
        }
      }
    }
  }
}
  

Комментарии:

1. Вы описали много кода, но было бы намного понятнее, если бы вы действительно могли показать код. Довольно сложно следить за тем, что происходит. В частности, ответ может очень легко зависеть от того, фиксируете ли вы какие-либо переменные.

2. Я понимаю. К сожалению, код настолько сложный и переработанный, что мне потребуется некоторое время, чтобы собрать все необходимые фрагменты и отфильтровать остальные, и, вероятно, вам потребуется столько же времени, чтобы связать их в уме. Тем не менее, я сделаю все возможное как можно скорее.

3. Если вы решили проблему, как кажется, вы решили, вы должны опубликовать решение в качестве ответа и пометить его как ответ. Это позволит другим быстрее узнать, что проблема решена.

Ответ №1:

Я решил проблему, изменив порядок инструкций и слегка переписав код в основном цикле. Переменной больше не присваивается значение null. Я подозреваю, что это было вызвано переупорядочением или вытеснением инструкции компилятора. Вот как это выглядит сейчас, не вызывая больше проблем:

 string url;
if (CollectedLinks.TryTake(out url))
{
  var queued = false;
  while (!queued)
  {
    // Loops thru the array looking for empty slot (null)
    for (byte i = 0; i < TaskArray.Length; i  )
    {
      if (TaskArray[i] == null)
      {
        TaskArray[i] = new Task(FindLinks, url, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
        TaskArray[i].Start(TaskScheduler.Current);
        queued = true; break;
      }
    }

    if (!queued)
    {
      // Loop and clean the array
      for (var i = 0; i < TaskArray.Length; i  )
      {
        if (TaskArray[i] == null)
          continue;
        if (TaskArray[i].Status == TaskStatus.RanToCompletion || TaskArray[i].Status == TaskStatus.Canceled || TaskArray[i].Status == TaskStatus.Faulted)
        {
          TaskArray[i].Wait(0);
          TaskArray[i] = null;
        }
      }
    }
  }
}