Вверх ↑
Ответов: 3927
Рейтинг: 620
#1: 2019-01-10 17:01:01 ЛС | профиль | цитата
Пример №1 работы параллельного потока:


Add(MainForm,8308775,154,126)
{
Height=214
Caption="Encrypt - threads demo"
BorderStyle=1
Position=1
}
Add(Edit,4011429,245,126)
{
Left=60
Top=5
Width=320
Text="123456"
}
Add(Label,15199179,245,77)
{
Left=10
Top=10
Width=48
Height=17
Caption="Пароль:"
}
Add(LineBreakEx,3037814,245,168)
{
Caption="pass"
Type=3
link(_Data,4011429:Text,[])
}
Add(Label,5968536,511,77)
{
Left=10
Top=35
Width=87
Height=17
Caption="Исходный файл:"
}
Add(Label,3369797,707,77)
{
Left=10
Top=75
Width=121
Height=17
Caption="Зашифрованный файл:"
}
Add(Button,5726157,147,581)
{
Left=10
Top=120
Width=95
Height=25
Caption="Зашифровать"
link(onClick,1595303:doStart,[])
}
Add(Label,14925684,756,574)
{
Left=80
Top=155
Width=10
Height=17
Caption="0"
}
Add(LineBreakEx,9758177,434,511)
{
Caption="fsrc"
Type=2
}
Add(LineBreakEx,5516860,441,532)
{
Caption="fencr"
Type=2
}
Add(LineBreakEx,10668305,427,490)
{
Caption="pass"
Type=2
}
Add(EnCrypt,13958992,420,574)
{
Mode=4
InitVector="AAA"
BufferSize=5242880
Point(onError)
Point(doAbort)
Point(onProgress)
link(onEncrypt,14941371:doWork2,[])
link(Key,10668305:getVar,[])
link(SrcFileName,9758177:getVar,[])
link(DstFileName,5516860:getVar,[])
link(onError,8933103:doMessage,[])
link(onProgress,14941371:doWork3,[(473,594)])
}
Add(Message,8933103,490,581)
{
Caption="Ошбика"
Icon=1
}
Add(Button,7998221,350,595)
{
Left=235
Top=150
Width=105
Caption="Прервать"
link(onClick,13958992:doAbort,[])
}
Add(HubEx,14941371,469,574)
{
link(onEvent,11140347:doSynchronize,[])
}
Add(Thread,1595303,217,581)
{
FastStop=0
link(onExec,13958992:doEncryptFile,[])
}
Add(Synchronize,11140347,707,574)
{
link(onSync,14925684:doText,[])
}
Add(Edit,824952,511,140)
{
Left=10
Top=50
Width=330
Text="bigfile.avi"
}
Add(Button,15277380,413,140)
{
Left=345
Top=50
Width=40
Caption="..."
link(onClick,1218516:doExecute,[])
}
Add(ODialog,1218516,469,140)
{
Filter="Все файлы (*.*)|*.*"
Title="Выбор файла"
FileName=""
Point(FileName)
link(onExecute,824952:doText,[])
link(FileName,12181497:Var3,[(475,125)(551,125)(551,187)])
}
Add(Edit,12991863,707,140)
{
Left=10
Top=95
Width=330
Text="bigfile.avi.encrypted"
}
Add(Button,4070287,609,140)
{
Left=345
Top=95
Width=40
Caption="..."
link(onClick,2080759:doExecute,[])
}
Add(ODialog,2080759,665,140)
{
Filter="Все файлы (*.*)|*.*"
Title="Выбор файла"
FileName=""
Point(FileName)
link(onExecute,12991863:doText,[])
link(FileName,10205981:Var3,[(671,121)(752,121)(752,180)])
}
Add(GetDataEx,10205981,707,175)
{
link(Data,12991863:Text,[])
}
Add(GetDataEx,12181497,511,182)
{
link(Data,824952:Text,[])
}
Add(LineBreakEx,9353820,511,203)
{
Caption="fsrc"
Type=3
link(_Data,12181497:Var2,[])
}
Add(LineBreakEx,1787963,707,196)
{
Caption="fencr"
Type=3
link(_Data,10205981:Var2,[])
}
Add(Label,6318026,154,210)
{
Left=10
Top=155
Width=68
Height=17
Caption="Обработано:"
}
Add(InfoTip,8165527,322,385)
{
Info=#207:Более правильно сначала через Synchronize (либо сразу по клику на кнопку) получить требуемые данные от визуальных компонентов в переменные, затем уже без Synchronize в параллельном потоке запустить обработку|
Width=253
Height=172
}
Add(InfoTip,2621263,651,490)
{
Info=#54:Обращаться к визуальным компонентам нужно только через|11:Synchronize|
Width=190
Height=151
}
Add(InfoTip,7225840,252,280)
{
Info=#10:Паттерн №1|84:Миниально корректное оповещение пользователя о прогрессе работы параллельного потока|
Font=[MS Sans Serif,8,1,0,1]
Width=393
Height=53
}
Здесь всё хорошо, но чем быстрее выдаются события EnCrypt.onEncrypt (например, от перехода с HDD на SSD или при уменьшении EnCrypt.BufferSize), тем более "дорогостоящим" оказывается вывод прогресса в Label, более тормознутым стаёт интерфейс, больше времени затрачивается на вывод, чем на саму обработку.
nesco писал(а):
у тебя никогда не возникало сомнения в точке Busy?
Думаю... Подумал.
Как по мне, так назначение этой точки - ровно то, что написано в её описании: "узнать, работает ли ещё поток, или уже завершился" (если это зачем-то нужно). А чем в это время он занимается (стоит ли на задержке, или качает файл из сети) - не важно: он занимается тем, что мы на него возложили (задержку и скачивание).
nesco писал(а):
мы могли бы свободно обращаться к данным параллельных потоков, пока те спят
Так в том то и дело что не могли. Потому что ошибки проявляются ровно в тот момент когда один поток "обращается к данным", а другой в этот же момент "просыпается" и пишет их. Задержка просто влияет на вероятность. А вот то "кольцо опроса" просто вносит дополнительный фактор неопределенности.

С другой стороны предложенный тобой пример использования вполне безопасно заменяется таймером в качестве "кольца опроса" и 2-мя SafeMode на чтение/запись данных для защиты данных при соместном доступе. Как раз хочу показать во втором "паттерне".

--- Добавлено в 2019-01-10 17:54:43

Пример №2 - правильное отображение прогресса работы параллельного потока:


Add(MainForm,8308775,154,126)
{
Height=214
Caption="Encrypt - threads demo"
BorderStyle=1
Position=1
}
Add(Edit,4011429,245,126)
{
Left=60
Top=5
Width=320
Text="123456"
}
Add(Label,15199179,245,77)
{
Left=10
Top=10
Width=48
Height=17
Caption="Пароль:"
}
Add(LineBreakEx,3037814,245,168)
{
Caption="pass"
Type=3
link(_Data,4011429:Text,[])
}
Add(Label,5968536,511,77)
{
Left=10
Top=35
Width=87
Height=17
Caption="Исходный файл:"
}
Add(Label,3369797,707,77)
{
Left=10
Top=75
Width=121
Height=17
Caption="Зашифрованный файл:"
}
Add(Button,5726157,140,819)
{
Left=10
Top=120
Width=95
Height=25
Caption="Зашифровать"
link(onClick,1595303:doStart,[])
}
Add(Label,14925684,854,1036)
{
Left=80
Top=155
Width=10
Height=17
Caption="0"
link(Text,3557433:Value,[])
}
Add(LineBreakEx,9758177,490,546)
{
Caption="fsrc"
Type=2
}
Add(LineBreakEx,5516860,497,567)
{
Caption="fencr"
Type=2
}
Add(LineBreakEx,10668305,483,525)
{
Caption="pass"
Type=2
}
Add(EnCrypt,13958992,476,819)
{
Mode=4
InitVector="AAA"
Point(onError)
Point(doAbort)
Point(onProgress)
link(onEncrypt,14941371:doWork2,[])
link(Key,10668305:getVar,[])
link(SrcFileName,9758177:getVar,[])
link(DstFileName,5516860:getVar,[])
link(onError,11356253:doEvent1,[])
link(onProgress,14941371:doWork3,[(529,839)])
}
Add(Message,8933103,595,854)
{
Caption="Ошбика"
Icon=1
}
Add(Button,7998221,336,847)
{
Left=235
Top=150
Width=105
Caption="Прервать"
link(onClick,4116247:doEvent1,[])
}
Add(HubEx,14941371,525,819)
{
link(onEvent,15768141:doSafeMode,[])
}
Add(Thread,1595303,210,819)
{
FastStop=0
link(onExec,9160381:doEvent1,[])
}
Add(Edit,824952,511,140)
{
Left=10
Top=50
Width=330
Text="bigfile.avi"
}
Add(Button,15277380,413,140)
{
Left=345
Top=50
Width=40
Caption="..."
link(onClick,1218516:doExecute,[])
}
Add(ODialog,1218516,469,140)
{
Filter="Все файлы (*.*)|*.*"
Title="Выбор файла"
FileName=""
Point(FileName)
link(onExecute,824952:doText,[])
link(FileName,12181497:Var3,[(475,125)(551,125)(551,187)])
}
Add(Edit,12991863,707,140)
{
Left=10
Top=95
Width=330
Text="bigfile.avi.encrypted"
}
Add(Button,4070287,609,140)
{
Left=345
Top=95
Width=40
Caption="..."
link(onClick,2080759:doExecute,[])
}
Add(ODialog,2080759,665,140)
{
Filter="Все файлы (*.*)|*.*"
Title="Выбор файла"
FileName=""
Point(FileName)
link(onExecute,12991863:doText,[])
link(FileName,10205981:Var3,[(671,121)(752,121)(752,180)])
}
Add(GetDataEx,10205981,707,175)
{
link(Data,12991863:Text,[])
}
Add(GetDataEx,12181497,511,182)
{
link(Data,824952:Text,[])
}
Add(LineBreakEx,9353820,511,203)
{
Caption="fsrc"
Type=3
link(_Data,12181497:Var2,[])
}
Add(LineBreakEx,1787963,707,196)
{
Caption="fencr"
Type=3
link(_Data,10205981:Var2,[])
}
Add(Label,6318026,154,210)
{
Left=10
Top=155
Width=68
Height=17
Caption="Обработано:"
}
Add(InfoTip,8165527,378,420)
{
Info=#207:Более правильно сначала через Synchronize (либо сразу по клику на кнопку) получить требуемые данные от визуальных компонентов в переменные, затем уже без Synchronize в параллельном потоке запустить обработку|
Width=253
Height=172
}
Add(LineBreakEx,6229770,357,707)
{
Caption="start"
}
Add(LineBreakEx,5156901,637,1036)
{
Caption="start"
Type=1
link(OnEvent,15007035:doTimer,[])
}
Add(InfoTip,7225840,252,294)
{
Info=#10:Паттерн №2|124:Уменьшен EnCrypt.BufferSize для ускорения выдачи событий и показано способ реализации оповещения пользователя в таком случае|
Font=[MS Sans Serif,8,1,0,1]
Width=442
Height=60
}
Add(Memory,3557433,854,819)
{
}
Add(SafeMode,15768141,784,819)
{
Name="CritSection1"
link(onSafeMode,3557433:doValue,[])
AddHint(-20,36,72,13,Name)
}
Add(SafeMode,15450595,791,1036)
{
Name="CritSection1"
link(onSafeMode,14925684:doText,[])
AddHint(-23,36,72,13,Name)
}
Add(InfoTip,16259543,749,770)
{
Info=#74:Данные, к которым обращаются несколько потоков, должны защищаться SafeMode|0:|0:|0:|0:|0:|0:|43:Здесь не следует заграгивать главный поток!|
Width=190
Height=151
}
Add(LineBreakEx,5684143,322,966)
{
Caption="stop"
}
Add(LineBreakEx,2541384,637,1057)
{
Caption="stop"
Type=1
link(OnEvent,15007035:doStop,[(691,1063)(691,1049)])
}
Add(Timer,15007035,700,1036)
{
Interval=500
Enable=1
link(onTimer,15450595:doSafeMode,[])
AddHint(-4,41,33,13,Interval)
}
Add(Hub,9160381,266,819)
{
OutCount=3
link(onEvent1,7172380:doSynchronize,[(294,825)(294,713)])
link(onEvent2,13958992:doEncryptFile,[])
link(onEvent3,5684143:doWork,[(293,839)(293,972)])
}
Add(Synchronize,7172380,308,707)
{
link(onSync,6229770:doWork,[])
}
Add(InfoTip,15317784,231,672)
{
Info=#64:Здесь Synchronize требуется для включения таймера из пар. потока|
Width=190
Height=81
}
Add(LineBreakEx,12946360,595,826)
{
Caption="stop"
}
Add(Hub,11356253,553,826)
{
link(onEvent1,12946360:doWork,[])
link(onEvent2,8933103:doMessage,[(582,839)(582,860)])
}
Add(LineBreakEx,13326123,406,847)
{
Caption="stop"
}
Add(Hub,4116247,378,847)
{
link(onEvent1,13326123:doWork,[])
link(onEvent2,13958992:doAbort,[(459,860)(459,846)])
}
Add(InfoTip,10198054,616,994)
{
Info=#28:Исполняется в главном потоке|
Width=281
Height=123
}
Add(InfoTip,2406579,308,896)
{
Info=#228:Поскольку таймер показывает пользователю прогресс с некоторым интервалом, то последнее значение (100%) пользователь может не увидеть - по завершению работы можно принудительно через Synchronize ещё раз вывести последнее значение|
Width=274
Height=102
}
- главный поток не перегружается вызовами из параллельного, а работает ровно с той нагрузкой, которую мы задали интервалом таймера и тяжестью выполняемой этим таймером работы. Независимо от загруженности параллельного потока.
- параллельный поток работает с максимальным быстродействием, не ограничиваясь скоростью работы визуальных компонентов. Можно предположить что есть некоторые издержки при "маршрутизации" системой потоков внутри критической секции, но они несущественны. Более важную роль играет трудоемкость действий, которые внутри этой секции происходят - следует минимизировать.

Хотите увидеть deadlock? Поставьте на выходе из Memory поле ввода Edit (или Synchronize) и уменьшите интервал таймера. Это кстати к вопросу о "вероятности ошибок" при использовании задержек - интервалом таймера можно добиться что программа какое-то время будет работать без deadlock. Но в какой-то момент все равно зависнет.
карма: 19

0
Редактировалось 7 раз(а), последний 2019-01-10 18:02:14