Текущий архив: 2007.03.25;
Скачать: CL | DM;
Вниз
Глобальные переменные в нитях Найти похожие ветки
← →
SpellCaster (2007-02-26 12:37) [0]Всем привет! Просветите, пожалуйста, насчёт использования глобальных переменных в нитях. Я-то всегда считал, что их можно беспрепятственно применять, если производится только чтение, но почитал одну ветку здесь и засомневался. Так действителдьно ли юзать переменные в нитях неправильно? А чем это может грозить?
← →
clickmaker © (2007-02-26 12:42) [1]критические секции юзай
потому как то, что ты читаешь, в этот же момент кто-то может менять
← →
Сергей М. © (2007-02-26 12:42) [2]
> действителдьно ли юзать переменные в нитях неправильно?
Неправильна сама постановка вопроса о "неправильности".
Правильно или неправильно - это зависит от полного понимания происходящего при этом.
> чем это может грозить?
Минимум - "глюками".
Максимум - крахом нити и/или процесса.
← →
DrPass © (2007-02-26 12:58) [3]
> Я-то всегда считал, что их можно беспрепятственно применять,
> если производится только чтение
Если речь идет об атомарной переменной (операции с которыми делаются "за один заход"), то можно - ничего с ней не сделается во время чтения. Со сложными типами следует быть осторожнее, тут уже все от логики приложения зависит. Если для чтения следует выполнить много операций, и есть другие потоки, которые могут что-либо писать туда - это действительно чревато ошибками.
← →
Степан (2007-02-26 17:41) [4]А какой лучший способ взаимодействия главного и побочного потоков? Я, например, использовал только PostMessage. Может есть другие способы?
← →
Игорь Шевченко © (2007-02-26 17:46) [5]
> А какой лучший способ взаимодействия главного и побочного
> потоков?
Рихтер целую главу (может, и не одну) написал про механизмы взаимодействия потоков в книжке "Windows для профессионалов". Может, его почитать ?
← →
Anatoly Podgoretsky © (2007-02-27 00:25) [6]> SpellCaster (26.02.2007 12:37:00) [0]
> Я-то всегда считал, что их можно беспрепятственно применять, если производится только чтение,
Это уже константа, а не переменная.
← →
SpellCaster (2007-02-28 11:23) [7]Поясняю ситуацию. Есть некий набор переменных, представляющий собой настройки проги. Они меняются только после закрытия формы настроек, в процессе работы проги они постоянны. По-моему, юзать их в потоках вполне безопасно - или я не прав? Просто критикалы или синхрониз юзать на каждое применение переменной не хочется. Можно, конечно, скопировать настройки в локальные переменные потока, но это тоже замороченный метод.
← →
SpellCaster (2007-02-28 11:26) [8]Кстати, а можете просветить вкратце: в чём собственно сама проблема наложения чтения/записи? Ведь в конечном итоге процессор всё равно выполняет только одну операцию в каждый конкретный момент времени.
> [5] Игорь Шевченко © (26.02.07 17:46)
А есть ссылочка?
← →
Сергей М. © (2007-02-28 11:30) [9]
> Ведь в конечном итоге процессор всё равно выполняет только
> одну операцию в каждый конкретный момент времени.
Присвоение значения строковой переменной - это далеко не одна процессорная операция.
← →
DrPass © (2007-02-28 11:32) [10]
> Кстати, а можете просветить вкратце: в чём собственно сама
> проблема наложения чтения/записи? Ведь в конечном итоге
> процессор всё равно выполняет только одну операцию в каждый
> конкретный момент времени.
Вполне конкретная - чтобы сохранить новое значение, например, в строковую переменную, процессору нужно выполнить сотни операций, вплоть до выделения нового блока памяти из кучи. И вероятность, что контекст потока переключится именно в это время, достаточно велика.
← →
Tor © (2007-02-28 12:01) [11]Сцуть заключается в следующем, нельзя читать когда пишут. Если настройки меняются тока впи закрытии окна, ну так останови поток запиши и двигай дальше. А вообще 10-раз обдумай целесообразность многопоточности, т.к. это сильно усложняет код проги, хотя по началу это не заметно. в конце концов критические секции это не долго. :)
← →
SpellCaster (2007-02-28 13:08) [12]> [9] Сергей М. © (28.02.07 11:30)
> [10] DrPass © (28.02.07 11:32)
Да, про строковые как-то не подумал.
> [11] Tor © (28.02.07 12:01)
Наверно, так и сделаю - приостановлю потоки на время изменения натсроек. Многопоточность нужна, т.к. это сетевое приложение, там куча всего качается и отсылается. С потоками раньше особо работать не приходилось, только на Яве, но на Яве это всё так завуалировано, что можно не париться про взаимодействие.
← →
SpellCaster (2007-02-28 13:32) [13]И еще вопросик про критические секции. Насколько я понял, покопавшись в сорсах VCL, их можно применять в любой процедуре. Вот такой способ - нормально?
procedure Log(s: string);
begin
Logger.Enter;
try
FormMain.Memo1.Lines.Add(s);
finally Logger.Leave; end;
end;
...
...Thread.Execute...
...
Log("Hi");
...
end
Также я не понял, какой смысл лочить в следующем фрагменте
function TCustomWinSocket.GetLocalAddress: string;
var
SockAddrIn: TSockAddrIn;
Size: Integer;
begin
Lock; // синоним Critical.Enter
try
Result := "";
if FSocket = INVALID_SOCKET then Exit;
Size := SizeOf(SockAddrIn);
if getsockname(FSocket, SockAddrIn, Size) = 0 then
Result := inet_ntoa(SockAddrIn.sin_addr);
finally
Unlock; // синоним Critical.Leave
end;
end;
← →
Сергей М. © (2007-02-28 14:05) [14]
> SpellCaster (28.02.07 13:32) [13]
А что тебя так заклинило именно на глоб.переменных ?
Существует масса иных способов заставить поток изменить хот выполнения алгоритма..
← →
SpellCaster (2007-02-28 15:21) [15]> [14] Сергей М. © (28.02.07 14:05)
Да не должен он ничего менять, просто при работе берутся кое-какие глобальные настройки.
← →
Сергей М. © (2007-02-28 15:29) [16]
> SpellCaster (28.02.07 15:21) [15]
ты даже не представляешь. какую глупость ты сейчас сморозил)
← →
SpellCaster (2007-02-28 16:21) [17]> [16] Сергей М. © (28.02.07 15:29)
ЗнаешЬ, может, я действительно туплю по-страшному, но не представляю совершенно. Так что скажешь насчет [13]?
← →
jack128 © (2007-02-28 17:44) [18]SpellCaster (28.02.07 13:32) [13]
Вот такой способ - нормально?
procedure Log(s: string);
begin
Logger.Enter;
try
FormMain.Memo1.Lines.Add(s);
finally Logger.Leave; end;
end;
...
...Thread.Execute...
...
Log("Hi");
...
end
Если это выполняется НЕ в основном потоке - то нет.
SpellCaster (28.02.07 13:32) [13]
Также я не понял, какой смысл лочить в следующем фрагменте
function TCustomWinSocket.GetLocalAddress: string;
var
SockAddrIn: TSockAddrIn;
Size: Integer;
begin
Lock; // синоним Critical.Enter
try
Result := "";
if FSocket = INVALID_SOCKET then Exit;
Size := SizeOf(SockAddrIn);
if getsockname(FSocket, SockAddrIn, Size) = 0 then
Result := inet_ntoa(SockAddrIn.sin_addr);
finally
Unlock; // синоним Critical.Leave
end;
end;
если FSocket меняется ТОЛЬКО в том же потоке, в котором выполняется этот код, то лочить естественно ничего не надо..
← →
SpellCaster (2007-02-28 17:57) [19]jack128
1) Хм. Что-то я не догоняю. Это процедура, которая вызывается из других нитей. А почему неправильно? И как правильно? Logger объявлен в главном модуле.
2) FSocket, как я понял, приватная переменная, и вроде бы не должна меняться в другой нити. Хотя кто их знает, борландовцев, что у них в голове - может, они специально сделали thread-safe класс.
← →
evvcom © (2007-03-01 10:51) [20]> [13] SpellCaster (28.02.07 13:32)
> ...Thread.Execute...
Что это вообще такое? Этот вызов за тебя уже сделает VCL. Не надо нарушать правила, чтобы твой код был логичен и понятен.
← →
Сергей М. © (2007-03-01 10:57) [21]
> что скажешь насчет [13]?
Какая переменная у тебя в этом коде глобальная ?
← →
SpellCaster (2007-03-01 11:14) [22]> [20] evvcom © (01.03.07 10:51)
Наверно, я непонятно написал... это просто заголовок метода Execute. Вот так вернее будет:
procedure TmyThread.Execute;
begin
...
Log("Hi");
...
end;
> [21] Сергей М. © (01.03.07 10:57)
Не, в этом коде переменных нету, это я уже про крит.секции спросил. А с переменными вот фрагмент:
...
Inc(CurrTry);
if CurrTry=Settings.Network_ConnectTry then
begin
WriteLog(Format("%d неудачных попыток - ждём %d сек.",
[Settings.Network_ConnectTry,Settings.Network_RetryInterv]),mkProc);
Sleep(Settings.Network_RetryInterv*1000);
CurrTry:=0;
end;
...
Settings - класс, хранящий настройки.
← →
evvcom © (2007-03-01 11:20) [23]> [22] SpellCaster (01.03.07 11:14)
Ах, вот оно что! Нельзя так делать. Своим Logger.Enter/Leave ты синхронизируешь только добавление строк в мемо из твоих потоков, вызывающих твой Log. Но ты забываешь, что при добавлении строки внутри этого Add происходит много различных действий, в том числе и перевыделение памяти под стринг, которое никак не синхронизировано с чтением в основном потоке. Используй Synchronize.
← →
Сергей М. © (2007-03-01 11:21) [24]
> SpellCaster (01.03.07 11:14) [22]
Т.е. Settings - это глобальная переменная типа TSomeClass ?
Тогда поясни, в каком потоке и в какой момент времени осуществляется модификация св-ва Network_ConnectTry ..
← →
SpellCaster (2007-03-01 14:34) [25]> [23] evvcom © (01.03.07 11:20)
Ага, ясно. Т.е. если из мемо ничего не считывать, то в принципе это прокатит - опасность начинается, когда начинаем работать с мемо из главной формы?
Хорошо, а как такой способ:
procedure thread.log(s: string);
begin
logger.enter;
form1.msg:=s;
synchronize(form1.log);
logger.leave;
end;
procedure form1.log;
begin
memo1.lines.add(msg);
end;
Критикал тут для того, чтобы другой поток в это время не трогал msg. Так верно будет?
> [24] Сергей М. © (01.03.07 11:21)
В основном, после нажатия кнопки ОК на форме настроек. Я сделал, как советовал Tor: ставлю все нити на паузу, обновляю настройки, а потом запускаю.
← →
Сергей М. © (2007-03-01 14:42) [26]
> SpellCaster (01.03.07 14:34) [25]
> В основном, после нажатия кнопки ОК на форме настроек
В этот момент другие потоки, обращающиеся к этому св-ву, существуют ?
> сделал, как советовал Tor
Хреновый совет.
С Boolean-данными это еще худо-бедно прокатит, но стоит применить сей способ для, к примеру, длинных строк - тут же схлопочешь те же самые вышеупомянутые грабли.
← →
SpellCaster (2007-03-01 15:24) [27]> В этот момент другие потоки, обращающиеся к этому св-ву,
> существуют ?
Да.
> Хреновый совет.
Ну почему же? Если нить приостановлена, то она и не обращается ни к каким переменным, верно?
← →
evvcom © (2007-03-01 15:52) [28]
> [25] SpellCaster (01.03.07 14:34)
> Ага, ясно. Т.е. если из мемо ничего не считывать, то в принципе
> это прокатит
Да нифига тебе не ясно. Не прокатит. Так как в основном потоке идет визуализация добавленных тобой строк, а значит и чтение, которым управлять ты в полной мере не можешь.
> form1.msg:=s;
> synchronize(form1.log);
Обычно делают проще.proc thread.Execute;
begin
...
msg := s;
synchronize(log);
...
end;
proc thread.Log;
begin
form1.memo1.lines.add(msg);
end;
← →
Сергей М. © (2007-03-01 16:01) [29]
> SpellCaster (01.03.07 15:24) [27]
> Да.
Тогда использование крит.секции - верный совет и верное решение.
> Если нить приостановлена, то она и не обращается ни к каким
> переменным, верно?
Вот смотри и вникай:
Некая нить в момент времени M начала чтение длинной строки, на этот момент содержимое строки равно, скажем, "AВС"
В момент времени N некая другая нить, приостановив первую нить (которая успела прочитать на этот момент только первые 2 символа строки, т.е. "АВ"), пишет в длинную строку "DEF" и возобновляет работу первой нити.
Что в рез-те получит первая нить ?
Да cолянку сборную она получит в виде "ABF" ! Хотя ожидалась либо "ABC" либо "DEF".. И это - в лучшем случае ... А в худшем читающая нить возбудит AV-исключение
← →
Сергей М. © (2007-03-01 16:02) [30]
> SpellCaster (01.03.07 15:24) [27]
> Да.
Тогда использование крит.секции - верный совет и верное решение.
> Если нить приостановлена, то она и не обращается ни к каким
> переменным, верно?
Вот смотри и вникай:
Некая нить в момент времени M начала чтение длинной строки, на этот момент содержимое строки равно, скажем, "AВС"
В момент времени N некая другая нить, приостановив первую нить (которая успела прочитать на этот момент только первые 2 символа строки, т.е. "АВ"), пишет в длинную строку "DEF" и возобновляет работу первой нити.
Что в рез-те получит первая нить ?
Да cолянку сборную она получит в виде "ABF" ! Хотя ожидалась либо "ABC" либо "DEF".. И это - в лучшем случае ... А в худшем читающая нить возбудит AV-исключение
← →
evvcom © (2007-03-01 16:05) [31]> [30] Сергей М. © (01.03.07 16:02)
Не... Это в лучшем случае программист при тестировании получит AV, а в худшем "ABF" он не заметит и отправит прогу пользователю. :)
← →
SpellCaster (2007-03-01 16:47) [32]> [28] evvcom © (01.03.07 15:52)
Так как в основном потоке идет визуализация добавленных тобой строк, а значит и чтение, которым управлять ты в полной мере не можешь.
Ладно, ладно, не стану морочиться, действительно сделаю как делал раньше - с двумя методами и synchronize.
> [29] Сергей М. © (01.03.07 16:01)
> Тогда использование крит.секции - верный совет и верное
> решение.
Хммм... только не в моем случае. Тормозить будет, да и много лишних строк получится.
> Вот смотри и вникай:
Ага. Посмотрел, проникся. Зарёкся юзать какие-либо межпотоковые переменные вообще )).
Наверно, просто при создании потока буду копировать нужные настройки в локальные threadvar. Уж они-то, вроде бы, безопасны?
← →
Сергей М. © (2007-03-01 16:56) [33]
> SpellCaster (01.03.07 16:47) [32]
> Тормозить будет
Не будет.
На то и крит.секции, чтобы минимум "тормозов" был при внутрипроцессной межпоточной синхронизации.
> при создании потока буду копировать нужные настройки в локальные
> threadvar. Уж они-то, вроде бы, безопасны?
Хрен редьки не слаще)
Безопасны-то они безопасны, но предназначены совсем для другой цели и работают они по-другому.
Что вообще тебе мешает передать значение настройки параметром в конструктор потока ?
← →
SpellCaster (2007-03-01 18:49) [34]> Не будет.
ОК, согласен. Но идея юзать критикалы для обращения к переменным как-то не очень прельщает...
Насчет threadvar: что-то с ними тоже какая-то засада. По крайней мере, строка не передавалась в функцию основного потока... ладно, сделал через внутренние переменные нити.
> Что вообще тебе мешает передать значение настройки параметром
> в конструктор потока ?
Да ничего, в общем-то... кроме того факта, что придётся делать ещё и процедуру апдейта этих настроек, поскольку они могут поменяться во время работы нити.
Страницы: 1 вся ветка
Текущий архив: 2007.03.25;
Скачать: CL | DM;
Память: 0.55 MB
Время: 0.039 c