![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Глобальные переменные в нитях
SpellCaster © (2007-02-26 12:37:00)Всем привет! Просветите, пожалуйста, насчёт использования глобальных переменных в нитях. Я-то всегда считал, что их можно беспрепятственно применять, если производится только чтение, но почитал одну ветку здесь и засомневался. Так действителдьно ли юзать переменные в нитях неправильно? А чем это может грозить?
SpellCaster (2007-03-01 18:49:00) [1]> Не будет.
ОК, согласен. Но идея юзать критикалы для обращения к переменным как-то не очень прельщает...
Насчет threadvar: что-то с ними тоже какая-то засада. По крайней мере, строка не передавалась в функцию основного потока... ладно, сделал через внутренние переменные нити.
> Что вообще тебе мешает передать значение настройки параметром
> в конструктор потока ?
Да ничего, в общем-то... кроме того факта, что придётся делать ещё и процедуру апдейта этих настроек, поскольку они могут поменяться во время работы нити.
Сергей М. (2007-03-01 16:56:00) [2]
> SpellCaster (01.03.07 16:47) [32]
> Тормозить будет
Не будет.
На то и крит.секции, чтобы минимум "тормозов" был при внутрипроцессной межпоточной синхронизации.
> при создании потока буду копировать нужные настройки в локальные
> threadvar. Уж они-то, вроде бы, безопасны?
Хрен редьки не слаще)
Безопасны-то они безопасны, но предназначены совсем для другой цели и работают они по-другому.
Что вообще тебе мешает передать значение настройки параметром в конструктор потока ?
SpellCaster (2007-03-01 16:47:00) [3]> [28] evvcom © (01.03.07 15:52)
Так как в основном потоке идет визуализация добавленных тобой строк, а значит и чтение, которым управлять ты в полной мере не можешь.
Ладно, ладно, не стану морочиться, действительно сделаю как делал раньше - с двумя методами и synchronize.
> [29] Сергей М. © (01.03.07 16:01)
> Тогда использование крит.секции - верный совет и верное
> решение.
Хммм... только не в моем случае. Тормозить будет, да и много лишних строк получится.
> Вот смотри и вникай:
Ага. Посмотрел, проникся. Зарёкся юзать какие-либо межпотоковые переменные вообще )).
Наверно, просто при создании потока буду копировать нужные настройки в локальные threadvar. Уж они-то, вроде бы, безопасны?
evvcom (2007-03-01 16:05:00) [4]> [30] Сергей М. © (01.03.07 16:02)
Не... Это в лучшем случае программист при тестировании получит AV, а в худшем 'ABF' он не заметит и отправит прогу пользователю. :)
Сергей М. (2007-03-01 16:02:00) [5]
> SpellCaster (01.03.07 15:24) [27]
> Да.
Тогда использование крит.секции - верный совет и верное решение.
> Если нить приостановлена, то она и не обращается ни к каким
> переменным, верно?
Вот смотри и вникай:
Некая нить в момент времени M начала чтение длинной строки, на этот момент содержимое строки равно, скажем, 'AВС'
В момент времени N некая другая нить, приостановив первую нить (которая успела прочитать на этот момент только первые 2 символа строки, т.е. 'АВ'), пишет в длинную строку 'DEF' и возобновляет работу первой нити.
Что в рез-те получит первая нить ?
Да cолянку сборную она получит в виде 'ABF' ! Хотя ожидалась либо 'ABC' либо 'DEF'.. И это - в лучшем случае ... А в худшем читающая нить возбудит AV-исключение
Сергей М. (2007-03-01 16:01:00) [6]
> SpellCaster (01.03.07 15:24) [27]
> Да.
Тогда использование крит.секции - верный совет и верное решение.
> Если нить приостановлена, то она и не обращается ни к каким
> переменным, верно?
Вот смотри и вникай:
Некая нить в момент времени M начала чтение длинной строки, на этот момент содержимое строки равно, скажем, 'AВС'
В момент времени N некая другая нить, приостановив первую нить (которая успела прочитать на этот момент только первые 2 символа строки, т.е. 'АВ'), пишет в длинную строку 'DEF' и возобновляет работу первой нити.
Что в рез-те получит первая нить ?
Да cолянку сборную она получит в виде 'ABF' ! Хотя ожидалась либо 'ABC' либо 'DEF'.. И это - в лучшем случае ... А в худшем читающая нить возбудит AV-исключение
evvcom (2007-03-01 15:52:00) [7]
> [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;
SpellCaster (2007-03-01 15:24:00) [8]> В этот момент другие потоки, обращающиеся к этому св-ву,
> существуют ?
Да.
> Хреновый совет.
Ну почему же? Если нить приостановлена, то она и не обращается ни к каким переменным, верно?
Сергей М. (2007-03-01 14:42:00) [9]
> SpellCaster (01.03.07 14:34) [25]
> В основном, после нажатия кнопки ОК на форме настроек
В этот момент другие потоки, обращающиеся к этому св-ву, существуют ?
> сделал, как советовал Tor
Хреновый совет.
С Boolean-данными это еще худо-бедно прокатит, но стоит применить сей способ для, к примеру, длинных строк - тут же схлопочешь те же самые вышеупомянутые грабли.
SpellCaster (2007-03-01 14:34:00) [10]> [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 11:21:00) [11]
> SpellCaster (01.03.07 11:14) [22]
Т.е. Settings - это глобальная переменная типа TSomeClass ?
Тогда поясни, в каком потоке и в какой момент времени осуществляется модификация св-ва Network_ConnectTry ..
evvcom (2007-03-01 11:20:00) [12]> [22] SpellCaster (01.03.07 11:14)
Ах, вот оно что! Нельзя так делать. Своим Logger.Enter/Leave ты синхронизируешь только добавление строк в мемо из твоих потоков, вызывающих твой Log. Но ты забываешь, что при добавлении строки внутри этого Add происходит много различных действий, в том числе и перевыделение памяти под стринг, которое никак не синхронизировано с чтением в основном потоке. Используй Synchronize.
SpellCaster (2007-03-01 11:14:00) [13]> [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 - класс, хранящий настройки.
Сергей М. (2007-03-01 10:57:00) [14]
> что скажешь насчет [13]?
Какая переменная у тебя в этом коде глобальная ?
evvcom (2007-03-01 10:51:00) [15]> [13] SpellCaster (28.02.07 13:32)
> ...Thread.Execute...
Что это вообще такое? Этот вызов за тебя уже сделает VCL. Не надо нарушать правила, чтобы твой код был логичен и понятен.
SpellCaster (2007-02-28 17:57:00) [16]jack128
1) Хм. Что-то я не догоняю. Это процедура, которая вызывается из других нитей. А почему неправильно? И как правильно? Logger объявлен в главном модуле.
2) FSocket, как я понял, приватная переменная, и вроде бы не должна меняться в другой нити. Хотя кто их знает, борландовцев, что у них в голове - может, они специально сделали thread-safe класс.
jack128 (2007-02-28 17:44:00) [17]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 16:21:00) [18]> [16] Сергей М. © (28.02.07 15:29)
ЗнаешЬ, может, я действительно туплю по-страшному, но не представляю совершенно. Так что скажешь насчет [13]?
Сергей М. (2007-02-28 15:29:00) [19]
> SpellCaster (28.02.07 15:21) [15]
ты даже не представляешь. какую глупость ты сейчас сморозил)
SpellCaster (2007-02-28 15:21:00) [20]> [14] Сергей М. © (28.02.07 14:05)
Да не должен он ничего менять, просто при работе берутся кое-какие глобальные настройки.