Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 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.57 MB
Время: 0.033 c
2-1173002800
br_ghost
2007-03-04 13:06
2007.03.25
Проблема с кириллицой


2-1173088934
Клара
2007-03-05 13:02
2007.03.25
Выпадающий список


3-1167142167
WondeRu
2006-12-26 17:09
2007.03.25
Можно ли передать ADOConnection в другой процесс?


15-1172434600
Nic
2007-02-25 23:16
2007.03.25
Покритикуйте дизайн сайт


2-1172740254
Riply
2007-03-01 12:10
2007.03.25
Утечка памяти при передаче нити дин. массива как параметра.