#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;
}
}
}
}
}