powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Один поток считает, другой - рисует - исключение
25 сообщений из 28, страница 1 из 2
Один поток считает, другой - рисует - исключение
    #38764270
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Поток интерфейса (поток 1) запускает расчёт в другом потоке (поток 2). Поток 2 после расчёта обновляет интерфейс (графики) и сразу начинает новый расчёт (в цикле). Т. е. операции расчёт-отрисовки происходят последовательно и в цикле. Похоже, что отрисовки иногда не успевают произойти, а следующий цикл расчёта уже стирает (Clear() по коллекции) данные, по которым происходят отрисовки, и кидается исключение "Collection was modified; enumeration operation may not execute". Это косвенно подтверждается тем, что чем мощнее процессор, на котором запускается программа, тем чаще кидается такое исключение - т. е. быстрее происходит следующая итерация расчёта и больше вероятность, что отрисовки не успеют произойти до этой следующей итерации.

Как сделать так, чтобы отрисовки успевали?
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764274
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
При этом:

1. Всё, что можно сделать со стороны отрисовок, я сделал (оптимизировал число отрисовываемых точек и прочее).

2. Вариант с засыпанием потока 2 не подходит, т. к. он запускает отрисовки (через Dispatcher), а делать это надо хотя бы раз в секунду. А на некоторых машинах и секунды мало для отрисовки данных после одной итерации расчёта.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764293
Фотография Алексей К
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп42Как сделать так, чтобы отрисовки успевали?Сделай отрисовку по таймеру. Выбери интервал таймера "чтобы успевало".
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764331
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей КНемоКэп42Как сделать так, чтобы отрисовки успевали?Сделай отрисовку по таймеру. Выбери интервал таймера "чтобы успевало".
Как выбирать этот интервал?

Штука в том, что количество за раз отрисовываемых данных может быть слишком большим даже с оптимизациями этих данных.

Есть ещё вариант делать реже расчёты. Но это тоже не подходит - реже раза в секунду уже не пойдёт.

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

Если назвать каждую итерацию расчёта кадром, то можно провести аналогию с рендерингом на видеокарте. Т. е. Процессор как можно быстрее подготавливает данные для кадров анимации, а видеокарта отрисовывает то, что успевает. Если не успевает, то кадры выбрасываются. Я подумал, что если в рендеринге такое используется, то почему бы и мне такую схему не задействовать? Хорошо я придумал?

Моей схемой, по-моему, можно обойти проблему синхронизации данных между потоками.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764335
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ещё забыл сказать, что один "результат" - это не одно значение, а несколько коллекций, в каждой из которых от сотни до тысячи значений. Т. е. каждый "кадр" результатов расчёта - такой вот снимок с этих коллекций.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764336
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
На каждой итерации расчёта эти коллекции либо изменяются, либо полностью затираются и заполняются новыми значениями.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764409
Ilya81
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если ошибка "Collection was modified; enumeration operation may not execute", то надо ставить блокировки. Оператор lock или класс ReaderWriterLock (как вариант ReaderWriterLockSlim). Ещё можете использовать ConcurrentBag, там можно без блокировок. Но лучше приведите код, в котором возникают эти ошибки, чтоб было понятнее, какое решение здесь подойдёт.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764434
Фотография Алексей К
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп42Я пока подумал, что неплохо бы накапливать последние результаты расчёта в буфер, а поток отрисовок будетДля отрисовок нужен не поток, а DispatcherTimer. Расчёты пусть живут своей жизнью в отдельном потоке. По таймеру берём результаты последнего расчёта и производим визуализацию. Не забыть синхронизировать доступ к результатам последнего расчёта.

зы: Это один из возможных вариантов, но не единственный.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764458
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ilya81Если ошибка "Collection was modified; enumeration operation may not execute", то надо ставить блокировки. Оператор lock или класс ReaderWriterLock (как вариант ReaderWriterLockSlim). Ещё можете использовать ConcurrentBag, там можно без блокировок. Но лучше приведите код, в котором возникают эти ошибки, чтоб было понятнее, какое решение здесь подойдёт.
С блокировками у меня не получается - я уже насоздавал тем по этому поводу.

У меня сейчас, если упрощённо, так

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
// myGraph - условно, UI-контрол в разметке XAML, могущий отрисовывать графики.
myGraph

// Результат расчёта.
List<double> results;

Thread t = new Thread(new ThreadStart(Calc));
t.IsBackground = true;
t.Start();

void Calc()
{
    for(...)
    {
        results = Calculation();
        Dispatcher.Invoke((Action)DrawGraph);
    }
}

void DrawGraph()
{
    myGraph.Redraw(results);
}

List<double> Calculation()
{
    List<double> results = ... // расчёт
    return results;
}
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764468
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп42У меня сейчас, если упрощённо, так

А я предложил переделать примерно (за синтаксис не ручаюсь) так

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
// myGraph - условно, UI-контрол в разметке XAML, могущий отрисовывать графики.
myGraph

// Результат расчёта.
Queue<List<double>> results;

Thread t = new Thread(new ThreadStart(Calc));
t.IsBackground = true;
t.Start();

void Calc()
{
    for(...)
    {
        results.Enqueue(Calculation());
        Dispatcher.Invoke((Action)DrawGraph);
    }
}

void DrawGraph()
{    
    int count = results.Count;
    
    // Отрисовываем последний результат расчёта.
    myGraph.Redraw(results.Last());
    
    // Удаляем все результаты, по последний на момент отрисовки.
    while(count > 0)
    {
        results.Dequeue();
        count--;
    }
}

List<double> Calculation()
{
    List<double> results = ... // расчёт
    return results;
}
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764471
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Неправильно написал. Надо разделить расчёт и отрисовки. Но суть в том, что я использую некий буфер (у меня - очередь) для хранения промежуточных результатов, и отрисовываю только последний набор результатов из буфера, после чего удаляю из этого буфера все предыдущие результаты по последний. И так в цикле.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764488
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как я понял Скайана , мне надо сделать так?

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
// myGraph - условно, UI-контрол в разметке XAML, могущий отрисовывать графики.
myGraph

Object thisLock = new Object();

// Результат расчёта.
List<double> results;

static void Main()
{
    Thread t = new Thread(new ThreadStart(Calc));
    t.IsBackground = true;
    t.Start();

    void Calc()
    {
        for(...)
        {
            lock(thisLock)
            {
                results = Calculation();
            }
            
            Dispatcher.Invoke((Action)DrawGraph);
        }
    }

    void DrawGraph()
    {
        lock(thisLock)
        {
            myGraph.Redraw(results);
        }
    }

    List<double> Calculation()
    {
        List<double> results = ... // расчёт
        return results;
    }
}
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764498
Arm79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп42Как я понял Скайана , мне надо сделать так?

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
// myGraph - условно, UI-контрол в разметке XAML, могущий отрисовывать графики.
myGraph

Object thisLock = new Object();

// Результат расчёта.
List<double> results;

static void Main()
{
    Thread t = new Thread(new ThreadStart(Calc));
    t.IsBackground = true;
    t.Start();

    void Calc()
    {
        for(...)
        {
            lock(thisLock)
            {
                results = Calculation();
            }
            
            Dispatcher.Invoke((Action)DrawGraph);
        }
    }

    void DrawGraph()
    {
        lock(thisLock)
        {
            myGraph.Redraw(results);
        }
    }

    List<double> Calculation()
    {
        List<double> results = ... // расчёт
        return results;
    }
}



Свеном бы сказал, что в данном случае lock избыточен, достаточно полю result добавить слово volatile. Я спорить не буду
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764544
Ilya81
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если results - поле класса, то зачем возвращать указатель на него из функции? Т. е. если это один и тот же экземпляр, то ошибка закономерна. Создайте копию коллекции и всё. Т. е. вызов конструктора того ж List с параметром results или функции ToArray/ToList из LINQ.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764700
НемоКэп422
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ilya81Если results - поле класса, то зачем возвращать указатель на него из функции? Т. е. если это один и тот же экземпляр, то ошибка закономерна. Создайте копию коллекции и всё. Т. е. вызов конструктора того ж List с параметром results или функции ToArray/ToList из LINQ.
Это я для простоты, чтобы покороче было. А вообще, results это поле другого класса, который создаётся где-то в Main. (И Main тоже для простоты - у меня там обработчик события на самом деле.)
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764719
немокэп422
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Arm79Свеном бы сказал, что в данном случае lock избыточен, достаточно полю result добавить слово volatile. Я спорить не буду
Да пипецваще. Тут гуры всякие боят всяких новичков (а они - боятся). Прям goto какой-то.

Когда в МСДНе volatile для одного значения применяется - это понятно. А когда для коллекции - никто толком не объясняет объясняет (ну, кроме комментов на SO). Из комментов на SO я могу сделать вывод, что мне надо именно назначать results новый экземпляр коллекции, если я буду использовать volatile.

А, штука в том, что на самом деле я не назначаю новый экземпляр. Реальный код ближе к этому - убрал lock, в цикле for теперь коллекция results очищается, а затем заполняется по-новому:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
// myGraph - условно, UI-контрол в разметке XAML, могущий отрисовывать графики.
myGraph

Object thisLock = new Object();

// Результат расчёта.
List<double> results;

static void Main()
{
    Thread t = new Thread(new ThreadStart(Calc));
    t.IsBackground = true;
    t.Start();

    void Calc()
    {
        for(...)
        {
            results.Clear();

            // На самом деле заполняется по-одному элементу по мере расчёта, 
            // но для краткости я тут написал так
            results.AddMany(Calculation());
            
            Dispatcher.Invoke((Action)DrawGraph);
        }
    }

    void DrawGraph()
    {
        myGraph.Redraw(results);
    }

    List<double> Calculation()
    {
        List<double> results = ... // расчёт
        return results;
    }
}
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764753
Ilya81
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп422Ilya81Если results - поле класса, то зачем возвращать указатель на него из функции? Т. е. если это один и тот же экземпляр, то ошибка закономерна. Создайте копию коллекции и всё. Т. е. вызов конструктора того ж List с параметром results или функции ToArray/ToList из LINQ.
Это я для простоты, чтобы покороче было. А вообще, results это поле другого класса, который создаётся где-то в Main. (И Main тоже для простоты - у меня там обработчик события на самом деле.)
Важно не где оно создаётся, а сколько раз. Если один раз и при этом в список что-то добавляется/удаляется, а другой поток может что-то читать из него - ошибка вполне закономерная. Говорю - простейшее решение - после заполнения в одном потоке в другой поток отдавать копию коллекции, а в исходном потоке после создания копии можно продолжать вносить изменения.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764845
немокэп422
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ilya81Важно не где оно создаётся, а сколько раз. Если один раз и при этом в список что-то добавляется/удаляется, а другой поток может что-то читать из него - ошибка вполне закономерная. Говорю - простейшее решение - после заполнения в одном потоке в другой поток отдавать копию коллекции, а в исходном потоке после создания копии можно продолжать вносить изменения.
Да, я выше подтвердил, что создаётся один раз и потом изменяется одним потоком, а считывается другим.

Ну так в том и проблема, что в каком потоке создавать копию и не будет ли изменения оригинала, пока эта копия создаётся? Если бы копия успевала создаваться, то и итерация тоже, наверное, успевала бы проходить, и исключения бы не было.

Если же копию должен создавать тот же поток, который изменяет оригинал, то это подходит.

Однако, возникает другой вопрос: а сколько копий нужно делать? Может, во время итерации вторым потоком по копии эта копия уже затрётся другой копией? Это в случае, если у нас копия - один экземпляр results, а не коллекция этих results.

Вот тут и возникает моя идея с очередью результатов (см. выше).
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764857
Arm79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
немокэп422Да пипецваще. Тут гуры всякие боят всяких новичков (а они - боятся). Прям goto какой-то.

Когда в МСДНе volatile для одного значения применяется - это понятно. А когда для коллекции - никто толком не объясняет объясняет (ну, кроме комментов на SO). Из комментов на SO я могу сделать вывод, что мне надо именно назначать results новый экземпляр коллекции, если я буду использовать volatile.

Давайте для начала я вас одну крамольную вещь скажу. result типа List<double> - это не список. Это УКАЗАТЕЛЬ на список. Фактически целое число. Поэтому volatile применяется грубо говоря к целочисленной переменной. Что это значит - что в разных потоках чтение из этого result приведет к считыванию самого последнего записанного значения. Если перестраховываться, то нужно всегда писать volatile для таких полей. Но обеспечение актуальности ссылки на список вовсе не означает, что сам список может быть актуальным, понимаете? В этом случае как раз используют lock.

Атомарность означает, что пока вы совершаете операцию чтения/записи с каким то полем или переменной или ссылкой, другой поток не может дописать что-либо в него. Например, тип Int64 - 8 байт. MSDN сообщает:
Код: plaintext
1.
2.
3.
Присвоение экземпляра данного типа не является потокобезопасным ни 
для одной аппаратной платформы, поскольку двоичное представление экземпляра 
может оказаться слишком большим для присвоения его в 
рамках одной атомарной операции.
Поэтому в многопоточной среде присваивать значение полю этого типа нужно через Interlocked

Иммутабельность, или неизменяемость - отношения к многопоточности не имеет никакого. Если брать самый известный пример - это строка. str1 = str1 + str2 - вовсе не означает, что к str1 будет прибавлено значение str2. Указанная строка означает, что берут значения str1 и str2, копируются в третью строку, ссылка на которую присваивается переменной str1. То есть старые значения str1 и str2 никак не меняются, и будут утилизированы GC.

Теперь по сабжу. Увы, я и WPF малознакомы, поэтому не могу уверенно утверждать, что совет полностью корректный. Но на WinForms фоновая работа обычно прекрасно разруливалась через BackgroundWorker.

И вас уже дали хороший совет - делайте копии коллекций, а не держите ссылку в поле.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38764924
Ilya81
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
немокэп422Да, я выше подтвердил, что создаётся один раз и потом изменяется одним потоком, а считывается другим.

Ну так в том и проблема, что в каком потоке создавать копию и не будет ли изменения оригинала, пока эта копия создаётся? Если бы копия успевала создаваться, то и итерация тоже, наверное, успевала бы проходить, и исключения бы не было.

Если же копию должен создавать тот же поток, который изменяет оригинал, то это подходит.

Однако, возникает другой вопрос: а сколько копий нужно делать? Может, во время итерации вторым потоком по копии эта копия уже затрётся другой копией? Это в случае, если у нас копия - один экземпляр results, а не коллекция этих results.

Вот тут и возникает моя идея с очередью результатов (см. выше).
Создавайте и пользуйтесь одним экземпляром коллекции в только том потоке, который формирует данные. Для передачи в другой поток создавайте копию и передавайте именно копию, притом каждый раз новую копию при передаче данных. Считывание выполняйте из копии коллекции. Дальше с этой копией ничего делать не надо, GC удалит эту копию из памяти после завершения чтения, т. е. когда указатель на эту копию выйдет из области его видимости.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38765646
НемоКэп42
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ilya81немокэп422Да, я выше подтвердил, что создаётся один раз и потом изменяется одним потоком, а считывается другим.

Ну так в том и проблема, что в каком потоке создавать копию и не будет ли изменения оригинала, пока эта копия создаётся? Если бы копия успевала создаваться, то и итерация тоже, наверное, успевала бы проходить, и исключения бы не было.

Если же копию должен создавать тот же поток, который изменяет оригинал, то это подходит.

Однако, возникает другой вопрос: а сколько копий нужно делать? Может, во время итерации вторым потоком по копии эта копия уже затрётся другой копией? Это в случае, если у нас копия - один экземпляр results, а не коллекция этих results.

Вот тут и возникает моя идея с очередью результатов (см. выше).
Создавайте и пользуйтесь одним экземпляром коллекции в только том потоке, который формирует данные. Для передачи в другой поток создавайте копию и передавайте именно копию, притом каждый раз новую копию при передаче данных. Считывание выполняйте из копии коллекции. Дальше с этой копией ничего делать не надо, GC удалит эту копию из памяти после завершения чтения, т. е. когда указатель на эту копию выйдет из области его видимости.
Выше Arm79 говорил про атомарные операции. Если уж простой double может не успеться записаться, то не может ли быть такого, что List<double> тем более не успеет скопироваться, пока его не изменят?

Далее, я ещё раз повторяю, а что будет, если чтение из копии другим потоком ещё не закончилось, а уже подошло время обновлять копию? Да ещё и несколько раз. Т. е. расчёт происходит в разы быстрее отрисовки, поэтому, наверное, надо на каждый расчёт хранить свою копию? И тут снова моя идея не с одной копией коллекции List<double>, а с несколькими копиями - Queue<List<double>>.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38765648
Arm79
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп42Далее, я ещё раз повторяю, а что будет, если чтение из копии другим потоком ещё не закончилось, а уже подошло время обновлять копию? Да ещё и несколько раз. Т. е. расчёт происходит в разы быстрее отрисовки, поэтому, наверное, надо на каждый расчёт хранить свою копию? И тут снова моя идея не с одной копией коллекции List<double>, а с несколькими копиями - Queue<List<double>>.

А зачем тебе постоянно пополняемая очередь, если приток значений будет выше, чем отток на отрисовку? У тебя же очередь будет постоянно расти, и в итоге на UI будут неактуальные данные...

НемоКэп42Если уж простой double может не успеться записаться, то не может ли быть такого, что List<double> тем более не успеет скопироваться, пока его не изменят?
Говорю же, делаешь lock, и никто к твоему списку не полезет...
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38765999
немокэп422
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Arm79НемоКэп42Далее, я ещё раз повторяю, а что будет, если чтение из копии другим потоком ещё не закончилось, а уже подошло время обновлять копию? Да ещё и несколько раз. Т. е. расчёт происходит в разы быстрее отрисовки, поэтому, наверное, надо на каждый расчёт хранить свою копию? И тут снова моя идея не с одной копией коллекции List<double>, а с несколькими копиями - Queue<List<double>>.

А зачем тебе постоянно пополняемая очередь, если приток значений будет выше, чем отток на отрисовку? У тебя же очередь будет постоянно расти, и в итоге на UI будут неактуальные данные...
Нет. Выше я подробнее говорил об этом - как только поток отрисовки считает из очереди последнее на текущий момент (на момент обращения на чтение) результат, он удалит все предыдущие, на момент обращения, результаты. Т. е., если в очереди 4 результата и поток отрисовки обращается к ней, то он берёт четвёртый результат и потом удаляет 4 результата, несмотря на то, что в очереди уже могли успеть появиться пара новых результатов. Эти пара новых результатов - предмет рассмотрения на следующей итерации отрисовки.

Т. е. итераций отрисовки будет меньше, чем итераций расчёта, в таком случае. Это считаю нормальным.

Arm79НемоКэп42Если уж простой double может не успеться записаться, то не может ли быть такого, что List<double> тем более не успеет скопироваться, пока его не изменят?
Говорю же, делаешь lock, и никто к твоему списку не полезет...
Так придётся лочить на один объект все места, где идёт работа с результатами - где они изменяются или считываются. И тут проблема, что мест этих обращений - много. Тогда либо лочить большие куски кода - возможно, даже всё тело цикла отрисовки - либо заморачиваться с кучей маленьких локов. В первом случае это может привести к тому, что код будет работать как бы однопоточно - два потока будут ждать друг друга по очереди. А во втором - ну, примерно то же самое, только с бОльшей частотой переключений между потоками, только ещё и проблем программисту прибавится - следить за всей этой кучей блокировок.

С накоплением результатов получается, что код работает на самом деле многопоточно. Хотя и с некоторыми дополнительными затратами по памяти.
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38766008
немокэп422
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
немокэп422С накоплением результатов получается, что код работает на самом деле многопоточно. Хотя и с некоторыми дополнительными затратами по памяти.
Тут может быть ещё проблема, что UI будет отображать результаты расчёта с недостаточной частотой. Т. е. может быть так, что расчёт закончится за время, когда UI успеет сформировать только один кадр.

Но тут уже другой подход. Тут я могу, например, усыплять потко расчёта на долю секунды каждый раз. Или просто поток расчёта может ждать данные по независящим от него причинам - ожидание пакетов данных из сети. В конце концов, можно настроить поток отрисовок на отрисовки всех накопившихся результатов подряд, без выкидывания их. Или выкидывать не все. - Возможностей масса.

Главное, что с накоплением результатов - т. е. с копированием результатов и работы потока отрисовок с коипей, как мне тут и советовали - я избавляюсь от многих проблем синхронизации.

Я тут читал, что более правильный принцип - не бороться с многопоточностью, изгаляясь по-всякому, пытаясь решить ошибки синхронизации, а применять более правильный дизайн. Т. е. если через приемлемые затраты по памяти можно избавиться от проблем синхронизации, то почему бы и нет?
...
Рейтинг: 0 / 0
Один поток считает, другой - рисует - исключение
    #38766059
Ilya81
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НемоКэп42Выше Arm79 говорил про атомарные операции. Если уж простой double может не успеться записаться, то не может ли быть такого, что List<double> тем более не успеет скопироваться, пока его не изменят?
Если Вы будете менять его только в одном потоке (который занимается расчётами) и в нём ж создавать копию, то скопироваться он успеет, ибо один поток не может выполнять два действия одновременно, такая ситуация возможна только при многопоточном доступе.
НемоКэп42И тут снова моя идея не с одной копией коллекции List<double>, а с несколькими копиями - Queue<List<double>>.
Очередь тут не нужна, ибо, как тут уже правильно заметили, пополнение будет происходить быстрее, чем выборка, и это может закончиться OutOfMemoryException (который даже не ловится в принципе). Вместо этого храните копию последней заполненной коллекции и выдавайте при запросе именно эту копию. После очередного заполнения просто заменяйте эту копию (старую после замены удалит garbage collector). К этому полю класса, в котором хранится указатель на копию коллекции, получается многопоточный доступ, но для указателя по идее должно хватить volatile, блокировки здесь вряд ли нужны.
НемоКэп42Но тут уже другой подход. Тут я могу, например, усыплять потко расчёта на долю секунды каждый раз. Или просто поток расчёта может ждать данные по независящим от него причинам - ожидание пакетов данных из сети.
Идея хорошая в плане оптимизации, но я б на Вашем месте начал б наиболее простого варианта, чтоб хотя б он для начала сработал. Потом, действительно, имеет смысл добавить ожидание, например, с помощью EventWaitHandle.
...
Рейтинг: 0 / 0
25 сообщений из 28, страница 1 из 2
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Один поток считает, другой - рисует - исключение
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]