Я хотел распараллелить кусок кода, но код на самом деле стал медленнее, вероятно, из-за издержек Barrier и BlockCollection.Там будет 2 потока, где первый найдет работы, над которыми будет работать второй.Обе операции не требуют больших усилий, поэтому издержки на безопасное переключение быстро перевесят два потока.
Так что я подумал, что сам попробую написать какой-нибудь код, чтобы он был как можно меньше, без использования барьера и т. Д.не вести себя согласованно, однако.Иногда это работает, иногда нет, и я не могу понять, почему.
Этот код - просто механизм, который я использую, чтобы попытаться синхронизировать два потока.Он не делает ничего полезного, только минимальный объем кода, необходимый для воспроизведения ошибки.
Итак, вот код:
// node in linkedlist of work elements
class WorkItem {
public int Value;
public WorkItem Next;
}
static void Test() {
WorkItem fst = null; // first element
Action create = () => {
WorkItem cur=null;
for (int i = 0; i < 1000; i++) {
WorkItem tmp = new WorkItem { Value = i }; // create new comm class
if (fst == null) fst = tmp; // if it's the first add it there
else cur.Next = tmp; // else add to back of list
cur = tmp; // this is the current one
}
cur.Next = new WorkItem { Value = -1 }; // -1 means stop element
#if VERBOSE
Console.WriteLine("Create is done");
#endif
};
Action consume = () => {
//Thread.Sleep(1); // this also seems to cure it
#if VERBOSE
Console.WriteLine("Consume starts"); // especially this one seems to matter
#endif
WorkItem cur = null;
int tot = 0;
while (fst == null) { } // busy wait for first one
cur = fst;
#if VERBOSE
Console.WriteLine("Consume found first");
#endif
while (true) {
if (cur.Value == -1) break; // if stop element break;
tot += cur.Value;
while (cur.Next == null) { } // busy wait for next to be set
cur = cur.Next; // move to next
}
Console.WriteLine(tot);
};
try { Parallel.Invoke(create, consume); }
catch (AggregateException e) {
Console.WriteLine(e.Message);
foreach (var ie in e.InnerExceptions) Console.WriteLine(ie.Message);
}
Console.WriteLine("Consume done..");
Console.ReadKey();
}
Идея состоит в том, чтобы иметь Linkedlist рабочих элементов.Один поток добавляет элементы в конец этого списка, а другой поток читает их, что-то делает и опрашивает поле Next, чтобы увидеть, установлено ли оно.Как только он установлен, он переместится на новый и обработает его.Он опрашивает поле Next в тесном цикле занятости, потому что оно должно быть установлено очень быстро.Переход в режим сна, переключение контекста и т. Д. Убьет выгоду от парализации кода.Время, необходимое для создания рабочего элемента, было бы вполне сопоставимо с его выполнением, поэтому потраченные впустую циклы должны быть весьма малы.
Когда я запускаю код в режиме выпуска, иногда он работает, иногда он ничего не делает.Кажется, проблема в потоке Consumer, поток Create всегда кажется завершенным.(Вы можете проверить, поигравшись с Console.WriteLines).Он всегда работал в режиме отладки.В релизе это около 50% хит и мисс.Добавление нескольких Console.Writelines помогает коэффициенту успеха, но даже в этом случае он не равен 100%.(#define VERBOSE материал).
Когда я добавляю Thread.Sleep (1) в поток 'Consumer', он также, кажется, исправляет это.Но неспособность воспроизвести ошибку - это не то же самое, что знать наверняка, что она исправлена.
Кто-нибудь здесь знает, что здесь происходит не так?Это какая-то оптимизация, которая создает локальную копию или что-то, что не обновляется?Что-то вроде того?
Нет такого понятия, как частичное обновление, верно?как данные, но тогда этот поток наполовину записывает, а другой поток читает частично записанную память?Просто проверяю ..
Глядя на это, я думаю, что это должно просто работать .. Я думаю, один раз каждые несколько раз потоки приходят в другом порядке, и это приводит к сбою, но я не понимаю, как.И как я мог это исправить, не добавляя замедления?
Заранее благодарим за любые советы,
Герт-Ян