Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Системное программирование и WinAPI > ...Эх потоки... пыль, да туман... |
Автор: Paspartu 22.6.2010, 23:54 | ||||||
Доброго времени суток! …Вот, разбираюсь с потоками, прочитал Дж. Рихтера, но возникли вопросы по синхронизации и вообще в каких случаях ее делать, так что вопрос чисто теоретический: Допустим есть функция : (.cpp):
И где-то класс CBaseThread который создает поток со static методом:
// Где-то в производном классе:
|
Автор: Earnest 23.6.2010, 07:04 | ||
Не совсем так. Если IsPrime читает переменные, которые могут изменять другие функции (конкурентно), то тоже нужно защищать. Даже простые переменные типа int. И вообще, пока не встали вопросы производительности, я бы в начале всех функций, которые могут вызываться из разных потоков, поставила блокировку. |
Автор: jonie 23.6.2010, 07:28 |
Earnest, чтение потокобезопасно. Лично я не вижу смысла ставить блокировку на чтение - на запись ставить надо. Паттерн UnitOfWork в общем с блокировкой типа "заблокировал и всё че надо поменял одним махом". В .NET например нет функций чтения Interlocked* - именно потому что всегда прочтенное значение будет именно то которое надо, как бы логически это не смотрелось неверным - просто один поток успеть может прочесть до изменения - но бловировка тут ничего не даст - он все-равно прочтет "старое" значение. |
Автор: GremlinProg 23.6.2010, 08:59 |
а Interlocked* и введены как раз для изменения значения, читать - понятно можно и без синхронизации, но вот если к примеру идет очень точный счет среди потоков, и они для этого счета используют общую переменную, которая постоянно меняется этими же потоками, то простое чтение без синхронизации может привести к неожиданным результатам: переменная еще не дописана потоком 1, а поток 2 уже начал ее читать, причем, когда поток 2 закончил ее читать, полток 1 только закончил в нее писать что получится? поток 2 может не получить не только нового значения переменной, но и не старого, т.е. поток 2 в момент расчета следующей итерации точно не опирается на расчеты потока 1 в таких случаях синхронизация нужна даже на чтение, да это не только атомарной переменной касается, вот например идет заполнение массива потоком 1, поток 2 может его спокойно читать без синхронизации, но что он в итоге прочитает, если для потока 2 массив актуален только в заполненном состоянии? |
Автор: xvr 23.6.2010, 09:00 | ||
Но может быть не атомарно, в таком случае не гарантируется целостность считанного значения |
Автор: jonie 23.6.2010, 10:58 | ||||
GremlinProg, xvr, читать можно без локов, всегда. Никакая атомарность чтения не проблема, т.к. запись атомарна (вот где локи нужны) - остальные ждут пока все запишется.
Чета я в общем сомневаюсь что вышеописанная неатомарность чтения на intel совместимых процах реализуема при должном обеспечении блокировки при записи Добавлено @ 11:00
В общем не надо путать проблемы дизайна решения и проблемы потоков. |
Автор: GremlinProg 23.6.2010, 11:04 | ||
это все правильно до тех пор, пока процессор и/или ядро в единственном экземпляре, т.е. в полном отсутствии параллелизма Добавлено через 1 минуту и 11 секунд в наше время это уже редкость |
Автор: Paspartu 23.6.2010, 11:06 | ||
Доброго времени суток! Всем огромное спасибо за разъяснение, но поправьте меня если я ошибаюсь: 1. Синхронизацию нужно проводить только при доступе различных потоков к общим данным Таким образом: a). Если 1-й поток читает к примеру массив то если в этот момент 2-й поток пытается его изменить то его нужно блокировать, пока чтение не будет завершено. б). Если 1-й пишет, а 2-й читает то нужна блокировка 2-го потока так что бы он читал уже измененные значения. Без нее ничего страшного не произойдет? Кроме того, что будет прочитано старое значение? в). Если оба потока читают – блокировка какого-либо из них не нужна. 2. Если происходит вызов (одной и той же) функции которая возвращает значение сколько угодными потоками то синхронизация не нужна так как общих данных в ней нет, а возвращаемое значение, ее параметры и ее локальные переменные будут для каждого потока своими? 3. Запутался с классами и их методами… если класс создан через new и в каждом потоке используется указатель на него, что будет копироваться в стек потока? К примеру, в разных потоках через указатель будет вызываться один и тот же метод, аналогично будут копироваться только возвращаемое значение, параметры метода и его локальные переменные? Или повторюсь, есть ли необходимость для каждого потока создавать свой экземпляр класса? т.е. есть класс:
Если при вызове методов F1, F2 их параметры будут копироваться в стек потока, то что будет с его членами m_n, m_db, m_ch? Если мы используем оди и тот же указатель на этот класс в разных потоках. В каких случаях нужна синхронизация методов F1, F2? Только если они изменяют значения членов класса? Нужно ли синхронизировать const или нет, т.к. он не изменяет объект? Опять же таки если в классе нет общих данных нужных нескольким потокам, к примеру в нем несколько методов, а члены int m_n и т.д. испоьзуются как промеж. Значения вычислений(образно)… можно ли при исопользовании одного экземляра для разных потоков обойтись без синхронизации, или она небходима, т.к. члены int m_n и т.д. все таки будут общими и они не будут копироваться в стек? Короче говоря… как бы с функциями вроде понятно, что они (значения локальных переменых и т.д.) копируются в стек, а если используется общий указатель на класс? Копирование в стек потока параметров и локальных переменных метода происходит в момент вызова потоком данного метода? А что происходит с членами класса (не методами) они становяться общими? А если мы в каждом потоке используем свой экземпляр то, синхронизация не нужна т.к. общих данных нет? … Много налил воды… но все же помогите разобраться раз и навсегда… наверное. ![]() |
Автор: GremlinProg 23.6.2010, 11:07 | ||
не-не, в одном потоке такого эффекта не получишь |
Автор: xvr 23.6.2010, 12:11 | ||
![]() |
Автор: xvr 23.6.2010, 12:29 | ||||||||||||||||
В случае если только 1 поток пишет данные, а все остальные читают, синхронизация может быть сильно упрощена (вплоть до полного отсуствия, если изменяемые данные имеют длинну 32 бита и выровненны на 4х байтовую границу) Может сильно помочь набор Interlocked* функций (уже упоминались). С ними можно избежать блокировки потоков
Члены классов (переменные) ничем не отличаются (для multithread'а) от обычных глобальных переменных. Все требования по сериализации доступа так же применимы и к ним NB. Обычно при разделении одной переменной между потоками сериализацию применяют ИМЕННО к переменной, а не к потокам. Пример:
Можно написать С++ класс (указатель), который будет сериализовать доступ к объекту, который он содержит
Использование:
|
Автор: Paspartu 23.6.2010, 12:33 |
Конечно, про 32-х и 64-х битные процессоры... это, конечно, все интересно ![]() ![]() |
Автор: jonie 23.6.2010, 12:34 |
xvr, хм, ладно убедили) |
Автор: Paspartu 23.6.2010, 12:41 |
Ура!!! ![]() xvr, С-П-А-С-И-Б-О !!! К сожалению, у меня не достаточно прав, на увеличение Вашего рейтинга, и все что могу - это еще раз спасибо за разъясгегие!!! ![]() Добавлено позже Ура!!! ![]() xvr, С-П-А-С-И-Б-О !!! К сожалению, у меня не достаточно прав, на увеличение Вашего рейтинга, и все что могу - это еще раз спасибо за разъясгегие!!! ![]() Добавлено через 3 минуты и 28 секунд Еще раз всем спасибо! Многое для меня прояснилось! |
Автор: GremlinProg 23.6.2010, 12:51 | ||
без проблем, xvr, +1 |
Автор: Earnest 23.6.2010, 15:45 |
Во понаписали-то, пока я своими делами занималась... И слава богу, а то, поверь, убеждаться в этом на собственной шкуре гораздо неприятнее. ![]() |
Автор: exploys 28.10.2010, 03:43 | ||||
Удобная штука. Единственное не DestroyCriticalSection(&cs); а DeleteCriticalSection(&cs); |