Вообще, при работе с параллельными потоками стоит обратить внимание на две вещи.
Первое - обращение (чтение-запись) к одним и тем же данным из различных потоков. Проблема возникает когда один поток пишет значение переменной, а второй либо пытается читать, либо тоже пишет. И если речь идет об одной переменной - проблемы маловероятны. Но мы работаем с компонентами, которые представляют собой класс. Простая запись значения приводит к вызову целой цепочки методов в дочерних и родительских классах, функций системы, изменения и проверки значений множества полей.
И вот когда один поток вызвал метод записи, цепочка которого выполнила часть изменений-проверок, но ещё не все, и в этот момент другой поток выполнил этот или другой метод, который затронул те же данные, с большой вероятностью произойдет какая-либо ошибка.
Стоит прояснить: в приложении всегда есть один главный поток. В этом главном потоке в частности исполняется цикл обработки сообщений оконного приложения. Все визуальные компоненты (а также другие компоненты, обрабатывающие сообщения, поступающие в эту очередь) получают команды и изменяют свое поведение во многом через сообщения, вызывая уже свои внутренние методы, как было описано выше.
И вот когда в параллельном потоке происходит какое-либо изменение визуального компонента и вызывается метод компонента, может происходить описанная выше ситуация конфликта, только в этом случае конкурирующим потоком стаёт главный поток, который, например, получил сообщение от системы и обработка этого сообщения затронула те же данные, что и параллельный поток.
Один из способов решения проблемы, возникающей с очередью сообщений и параллельными потоками, применяется в компоненте потока KOL и используется представленным InlineCode: метод doSynchronize и событие onSync происходят путем посылки оконного сообщения в главный поток. При этом механизм сообщений Windows предполагает, что пока это сообщение не будет обработано, ни одно другое сообщение не перебъет его исполнение, в том числе если сколько угодно других потоков тоже вызовут метод doSynchronize.
Таким образом мы получаем некоторый контроль над доступом параллельных потоков к визуальным компонентам.
Второе - когда параллельные потоки одновременно работают с невизуальными компонентами. Более общий вариант предыдущего пункта. Поскольку тут нет сообщений, применение предыдущего метода синхронизации хоть и во многих случаях помогает (потоки выполняются последовательно, не перебивая друг друга), но оно имеет свои недостатки:
- в неоконных приложениях нет главной очереди сообщений, поэтому до недавнего времени компоненты и схемы с параллельными потоками не имели возможность использовать событие onSyncExec (и предложенный InlineCode). Для этих случаев я предложил компонент MainLoop.
- когда потоков много или они слишком часто делают doSynchronize, это фактически переводит многопоточное приложение в однопоточный режим, поскольку потоки стают в очередь и выполняются последовательно. Косвенно это можно заметить и по "тормозам" в интерфейсе приложения.
Вот для решения второго пункта используется минимальная защита только того участка схемы, к которому совместно обращаются несколько параллельных потоков. Для этого используются другие механизмы системы - критические секции, мютексы, сигналы.
Обратите внимание на вкладку "Система"->"Потоки". Подробно тут описывать конкретное применение компонентов не буду, потому что и сам туда не заглядывал. Расскажу только принцип работы критической секции.
Когда нужно оградить некоторый участок схемы от одновременного доступа из нескольких потоков, например, требуется вызвать метод некоторого компонента, перед этим методом мы ставим компонент SafeMode (Mode=Local) и заводим все потоки на этот компонент. Если вдруг два потока вызвали наш метод одновременно, система сама ставит их в очередь и первый по порядку поток выполняет этот метод. Когда он закончит - система пропустит второй поток к этому методу. Таким образом разрушения данных не произойдет. В теории. С некоторой вероятностью. Так как на практике у нас весь код компонентов скрытый, и иногда может быть сложно определить, какие данные какой компонент изменяет и в каком месте надо поставить критическую секцию.
В общем, написал много, не могу гарантировать, что всё правильно. Надеюсь, кому-нибудь поможет.
Ответов: 4628
Рейтинг: 749
|
|||
карма: 26 |
| ||
Голосовали: | Neo, flint2 |
Редактировалось 1 раз(а), последний 2017-03-20 12:39:18