Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2014.05.04;
Скачать: [xml.tar.bz2];

Вниз

СТРОКИ НЕ ПОТОКОБЕЗОПАСНЫ   Найти похожие ветки 

 
Пит   (2013-11-01 22:19) [80]


> Отличается что?

WideString от UnicodeString


> Подсчета ссылок в WideString кстати нет вообще

да?! Вот это новость... Я был уверен, что WideString - полная копия AnsiString, только символ двухбайтный.


 
DVM ©   (2013-11-01 22:23) [81]


> Пит   (01.11.13 22:19) [80]


> да?! Вот это новость... Я был уверен, что WideString - полная
> копия AnsiString, только символ двухбайтный.

не, это не копия, и это очень хорошо, особенно хорошо такие строки передавать из в длл. Менеджер памяти то один получается и в длл и в программе.

вот тут один человек пытается добавить в WideString строки подсчет ссылок и дельфийский менеджер памяти: http://dign.narod.ru/articles/fastwidestring/


 
Владислав ©   (2013-11-02 02:24) [82]

DVM ©   (01.11.13 14:17) [56]

> Владислав ©   (01.11.13 12:05) [52]
> DVM ©   (01.11.13 10:14) [48]
> > Строка будет скопирована, поток будет работать с копией.
>
>
> С чего бы вдруг?

> с того, что по значению передается

Жуть какая. Не уж то открытие?.. Ну с новыми знаниями!


 
DVM ©   (2013-11-02 10:34) [83]


> Владислав ©   (02.11.13 02:24) [82]

> Жуть какая.


Что вас так удивило? Что передается по значению? Да, именно по значению, а не по ссылке, хоть string и является ссылочным типом данных.

Владислав, прочитайте всю ветку, ряд типов строк (ShortString, WideString) будет скопированы немедленно при передаче функцию таким образом (см асм код и модуль system). Те, что не будут скопированы при передаче немедленно, все равно будут скопированы по месту, где их попытаются изменить (посмотрите асм код), если их будут менять как строки (!) (например, так s := s + "a" или так s := "a" или так SetLength(a, 10) и т.д.), а не как массивы, обращаясь к отдельным символам или по указателю на них как области памяти.

Примитивный пример:

procedure Test(s: string);
var
 a: string;
begin
 a  := s;
 a := a + "5";
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 Test(s);
end;


Его ASM вариант такой:

Unit1.pas.29: a  := s;
0051130C 8D45F8           lea eax,[ebp-$08]
0051130F 8B55FC           mov edx,[ebp-$04]
00511312 E8795CEFFF       call @UStrLAsg
Unit1.pas.30: a := a + "5";
00511317 8D45F8           lea eax,[ebp-$08]
0051131A BA58135100       mov edx,$00511358
0051131F E87865EFFF       call @UStrCat


UStrCat находится в System и копирует содержимое строки при необходимости. Тут нет никакой проблемы при многопоточном доступе, изменяться всегда будут разные строки.

Согласен, некоторая ненулевая вероятность того, что компилятор построит код, в котором копирования не произойдет перед изменением имеется, но такие ситуацие редки.


 
DVM ©   (2013-11-02 10:45) [84]

Отсюда вытекает [10]. Если на одну и ту же строку имеется несколько ссылок, то по разным ссылкам можно смело обращаться к строке из нескольких потоков, т.к подсчет ссылок сам по себе атомарен, а перед любым изменением строки на которую есть еще ссылки гарантированно произойдет копирование этой строки. При условии, что мы к строкам обращаемся как именно к строкам, а не массивам или указателям.


 
Владислав ©   (2013-11-02 13:31) [85]

DVM ©   (02.11.13 10:34) [83]

Не пишите ерунду. При передаче строки в качестве параметра строка не копируется. А модификатор const или его отсутствие влияют на изменение счетчика ссылок.
И к чему столько рассуждений и примеров на тему присвоения строке нового значения?
Всю тему перечитывать не нужно. Перечитайте 48 и 56. Можете даже написать 48 и заглянуть в asm view.


 
DVM ©   (2013-11-02 14:20) [86]


> Владислав ©   (02.11.13 13:31) [85]


> Не пишите ерунду. При передаче строки в качестве параметра
> строка не копируется.

Где ерунда. Зависит от строки. WideString скопируется. ShortString скопируется.


procedure Test(s: WideString);
var
 a: WideString;
begin
 a := s;
end;


в CPU View это выглядлит как:


Unit1.pas.29: a := s;
00511314 8D45F8           lea eax,[ebp-$08]
00511317 8B55FC           mov edx,[ebp-$04]
0051131A E8C55CEFFF       call @WStrLAsg


WStrLAsg приведет нас под Windows в результате к SysReAllocStringLen, которая создает новую строку BSTR  и копирует данные из старой, как следует из MSDN.

Аналогично для ShortString, только там будет вызвана PStrCpy.

Непосредственно только при передаче строка AnsiString и UnicodeString не копируется конечно, своей фразой я не это хотел сказать. Она скопируется позже при попытке ее изменить при наличии еще ссылок на нее.


> А модификатор const или его отсутствие влияют на изменение
> счетчика ссылок.

Вот именно, а в зависимости от того, если или нет ссылки, строка будет копироваться или не будет при попытке ее изменить.

Мы же с чего речь начали, с того, что пытались выяснить насколько потокобезопасна такая конструкция, вот я и написал, что при передаче по значению строка в результате будет все равно скопирована не сразу так позже и проблемы одновременного доступа к ней из разных потоков нету. Поток с копией будет работать.


> Можете даже написать 48 и заглянуть в asm view.

Да глядел я. Мы о разном говорим.


 
DevilDevil ©   (2013-11-02 20:13) [87]

Удалено модератором
Примечание: Личная пере(бран)писка


 
картман ©   (2013-11-02 21:12) [88]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Так, закрываемся!   (2013-11-02 23:47) [89]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 01:47) [90]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 01:49) [91]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Sapersky   (2013-11-03 02:46) [92]

Удалено модератором
Примечание: Личная пере(бран)писка


 
DevilDevil ©   (2013-11-03 03:41) [93]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 03:43) [94]

Удалено модератором
Примечание: Личная пере(бран)писка


 
DevilDevil ©   (2013-11-03 03:44) [95]

Удалено модератором
Примечание: Личная пере(бран)писка


 
DevilDevil ©   (2013-11-03 03:45) [96]

Удалено модератором
Примечание: Личная пере(бран)писка


 
Владислав ©   (2013-11-03 03:49) [97]

DevilDevil ©   (03.11.13 03:41) [93]

Буквоед. Есть такое. Я в [94] объяснил, почему.

Вот ты пишешь:
constructor TMyClass.Create(const AHost: string);
begin
 FHost := AHost; // брейк поинт здесь
end;


А сам то брекпоинт ставил? И что, реально копирование происходит?.. А если проследовать во все вызываемые процедуры?..


 
DevilDevil ©   (2013-11-03 03:50) [98]

Удалено модератором
Примечание: Бан неизбежен как кризис империализма


 
DevilDevil ©   (2013-11-03 03:59) [99]

и да, случай чтения или копирования константной строки - идеальный с точки зрения потокобезопасности


 
Владислав ©   (2013-11-03 04:01) [100]

DevilDevil ©   (03.11.13 03:50) [98]

Ткни, конечно. Я, как и все остальные, тоже могу ошибаться.

DevilDevil ©   (03.11.13 03:59) [99]

Сразу извини за манеру подачи. Не пиши ерунды.


 
DevilDevil ©   (2013-11-03 04:08) [101]

> Сразу извини за манеру подачи. Не пиши ерунды.
> Ткни, конечно. Я, как и все остальные, тоже могу ошибаться.

у меня Delphi7, у меня код идёт в _LStrAsg
там он попадает на CALL _NewAnsiString и CALL Move

Если у тебя Unicode-версия Delphi, то придти ты должен в _UStrAsg
может быть ещё в _UStrAddRef но там он проработает вхолостую


 
Владислав ©   (2013-11-03 04:12) [102]

DevilDevil ©   (03.11.13 04:08) [101]

> у меня Delphi7, у меня код идёт в _LStrAsg
> там он попадает на CALL _NewAnsiString и CALL Move

Можно пример вместе с кодом на паскале? Уж ткнуть носом, так ткнуть.


 
DevilDevil ©   (2013-11-03 04:16) [103]

> Владислав ©   (03.11.13 04:12) [102]

а ты не знаешь как в System в дебаге зайти что ли ?

procedure _LStrAsg(var dest; const source);
{$IFDEF PUREPASCAL}
var
 S, D: Pointer;
 P: PStrRec;
 Temp: Longint;
begin
 S := Pointer(source);
 if S <> nil then
 begin
   P := PStrRec(Integer(S) - sizeof(StrRec));
   if P.refCnt < 0 then   // make copy of string literal
   begin
     Temp := P.length;
     S := _NewAnsiString(Temp);
     Move(Pointer(source)^, S^, Temp);
     P := PStrRec(Integer(S) - sizeof(StrRec));
   end;
   InterlockedIncrement(P.refCnt);
 end;

 D := Pointer(dest);
 Pointer(dest) := S;
 if D <> nil then
 begin
   P := PStrRec(Integer(D) - sizeof(StrRec));
   if P.refCnt > 0 then
     if InterlockedDecrement(P.refCnt) = 0 then
       FreeMem(P);
 end;
end;
{$ELSE}
asm
       { ->    EAX pointer to dest   str      }
       { ->    EDX pointer to source str      }

               TEST    EDX,EDX                           { have a source? }
               JE      @@2                               { no -> jump     }

               MOV     ECX,[EDX-skew].StrRec.refCnt
               INC     ECX
               JG      @@1                               { literal string -> jump not taken }

               PUSH    EAX
               PUSH    EDX
               MOV     EAX,[EDX-skew].StrRec.length
               CALL    _NewAnsiString
               MOV     EDX,EAX
               POP     EAX
               PUSH    EDX
               MOV     ECX,[EAX-skew].StrRec.length
               CALL    Move
               POP     EDX
               POP     EAX
               JMP     @@2

@@1:
          LOCK INC     [EDX-skew].StrRec.refCnt

@@2:            XCHG    EDX,[EAX]
               TEST    EDX,EDX
               JE      @@3
               MOV     ECX,[EDX-skew].StrRec.refCnt
               DEC     ECX
               JL      @@3
          LOCK DEC     [EDX-skew].StrRec.refCnt
               JNE     @@3
               LEA     EAX,[EDX-skew].StrRec.refCnt
               CALL    _FreeMem
@@3:
end;
{$ENDIF}


 
Владислав ©   (2013-11-03 04:26) [104]

DevilDevil ©   (03.11.13 04:16) [103]

Ты удалаешься от [48].
Ну уж сказал А, говори Б.
Уже ткни меня носом, в каком месте копирование строки?


 
DevilDevil ©   (2013-11-03 04:33) [105]

> Владислав ©   (03.11.13 04:26) [104]

я же тебе буквами по форуму написал
1) тестируешь написанный мной в [93] код
2) останавливаешься в брейкпоинте на указанное место
3) трейсишь в недра
4) наблюдаешь NewAnsiString и Move

что не ясно ?


 
Владислав ©   (2013-11-03 04:43) [106]

DevilDevil ©   (03.11.13 04:33) [105]

Ты меня немного утомил. Ты не мой ученик, а я не твой учитель, чтобы тебе "жевать и в рот складывать".
Воспользуйся своим алгоритмом, и уразумей таки.
Подсказка тебе: ты точно понимаешь смысл вот этой строки в приведенном тобой коде:
if P.refCnt < 0 then   // make copy of string literal
?
Там даже комментарий есть. Ты и вправду не вникаешь?


 
DevilDevil ©   (2013-11-03 04:47) [107]

Тяжёлый случай :)
Ладно, я спать :)


 
Sapersky   (2013-11-03 06:44) [108]

Константы, сэр...

И кстати, я сформулировал принцип "частичной потокобезопасности" строк.
Это вообще не потокобезопасность.
Это обеспечение элементарной логики, согласно которой РАЗНЫЕ переменные, используемые в разных потоках, не должны друг от друга зависеть.
В случае строк разные переменные могут фактически указывать на одну и ту же строку, и RTL пытается по возможности это скрыть. Работает это почти всегда, за исключением случаев, когда разработчик, злобный буратина, пишет в "неявно общую" строку через PChar (при обращениях вроде s[n] должна вызываться UniqueString).

В принципе, DVM с самого начала говорил что-то в этом роде - "c чего ты взял, что строки потокобезопасны" и т.д.


 
DVM ©   (2013-11-03 11:02) [109]


> Владислав


> Для того, чтобы "огрести" по полной с потоками и верой,
> что строки копируются, достаточно написать тестовый пример,
>  используя совершенно стандартное приведение типов:
> Изменение строки:
> PChar(StringVariable)^ := "B"

Так разумеется нельзя, я об этом писал [62,83,84], проблемы могут быть. Никаких указателей. Такой случай в данном контексте не рассматривается.


> Владислав ©   (03.11.13 03:43) [94]


>  Кроме всего прочего, само копирование не потокобезопасно.
>  

Само по себе конечно не безопасно, я писал там выше про UniqueString [11]. Но тут дело в том, что в нашем случае ВСЕ вызовы изменения строки будут обернуты таким кодом, что перед изменением всегда будет создаваться копия. А это означает, что оригинал никто не будет править, все будут править свои копии. Ведь так? Но при соблюдении определенных условий (без указателей и проч.). Приведенный Пит-ом пример как раз идеальный для иллюстрации этого.
Это при разумной организации логики программы конечно. Ситуаций когда один поток производит изменение строки, а в это время другой поток получает ссылку на ту же строку и начинает создавать себе копию не должно получаться, если строки в потоки передают как описано выше, а не по указателям.


> Sapersky   (03.11.13 06:44) [108]


> И кстати, я сформулировал принцип "частичной потокобезопасности"
> строк.

Ну в общем случае, да, получается так.


> при обращениях вроде s[n] должна вызываться UniqueString

Я посмотрел, менять отдельные символы безопасно, тоже создается копия при необходимости, так что из [62,83,84] массив можно вычеркнуть. Т.о опасным остается только приведение к указателям и изменение по указателям.


 
DVM ©   (2013-11-03 13:06) [110]


> Владислав ©   (03.11.13 04:43) [106]


> Подсказка тебе: ты точно понимаешь смысл вот этой строки
> в приведенном тобой коде:
> if P.refCnt < 0 then   // make copy of string literal
> ?
> Там даже комментарий есть. Ты и вправду не вникаешь?

Я вот сейчас проверил в D7, все именно так, как описывает DevilDevil, есть разница по всей видимости между присвоением полю класса, через метод класса и просто переменной внутри обычной функции.

Тестовый код такой:

TMyClass = class
 private
   FFileld: string;
 public
   constructor Create(const S: string);
 end;

{ TMyClass }

constructor TMyClass.Create(const S: string);
begin
 FFileld := S;
end;


Вызываем так:

procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 TMyClass.Create(S);
end;


Точку останова ставим на   FFileld := S; включаем UseDebugDCU запускаем по F7 входим в _LStrAsg, там попадаем на

CALL    _NewAnsiString

Которая создает новую строку, а затем Move копирует данные.

----------------------------------------------------------------

Пример 2:



procedure Test(const s: AnsiString);
var
 a: AnsiString;
begin
 a := s;
end;


Вызываем:

procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 Test(s);
end;


Попадаем в _LStrLAsg, в ней нет копирования, там только счетчик ссылок меняется.


 
Владислав ©   (2013-11-03 19:15) [111]

DVM ©   (03.11.13 13:06) [110]

Не передавайте туда литерал, передайте нечто подобное: StringOfChar("1", 10).

А то Вы мне доказываете, что при присвоении литералы копируются. Так это понятно, _LStrLAsg даже соответствующие комментарии имеет, но речь то не об этом.
Вы разобрались, почему у Вас в примере 1 срока скопировалась, а в примере 2 нет?


 
DVM ©   (2013-11-03 22:26) [112]


> Владислав ©   (03.11.13 19:15) [111]


> Вы разобрались, почему у Вас в примере 1 срока скопировалась,
>  а в примере 2 нет?

Очевидно, что дело в области видимости переменных, которым присваивается значение. Для локальных вызываеся функция _LStrLAsg, у которой L в названиии и обозначает Local, для остальных _LStrAsg.

По всей видимости, функция _LStrLAsg вызывается для локальных переменных по той причине, что для них нерационально делать полное копирование в виду того, что такая переменная будет жить ограниченное время и видимость ее ограничена функцией.

Если же переменная имеет шанс использоваться и в дальнейшем после присваивания (глобальная переменная или поле класса), то в нее копируется значение. Это более надежно и безопасно, особенно если литерал живет в DLL или пакете, который может быть выгружен и в поле класса или глобальной переменной останется ссылка в никуда.


 
Владислав ©   (2013-11-03 22:41) [113]

Кошмар...


 
DVM ©   (2013-11-03 22:52) [114]


> Владислав ©   (03.11.13 22:41) [113]

Есть другое объяснение? Просим...


 
Владислав ©   (2013-11-03 23:27) [115]

Там все же написано.
Комментарии к функции _LStrAsg из System.pas:
{ 99.03.11
 This function is used when assigning to global variables.

 Literals are copied to prevent a situation where a dynamically
 allocated DLL or package assigns a literal to a variable and then
 is unloaded -- thereby causing the string memory (in the code
 segment of the DLL) to be removed -- and therefore leaving the
 global variable pointing to invalid memory.
}:
В LStrAsg копия создается только для литералов. Зачем это сделано, написано в комментарии.


 
DVM ©   (2013-11-03 23:33) [116]


> Владислав ©   (03.11.13 23:27) [115]


> Там все же написано.

Я это видел. И что? В  [110] у меня и в первом варианте исходная строка литерал и во втором литерал, однако в первом копируется (_LStrAsg) во втором не копируется, а лишь увеличивает счетчик ссылок (_LStrLAsg). Вас ведь это интересовало? А разница именно в том, что в первом варианте строка приемник - поле класса, а во втором локальная переменная.


 
DVM ©   (2013-11-03 23:35) [117]


> Владислав ©   (03.11.13 23:27) [115]

И потрудитесь ответить в чем состоит смысл вашего:

> Владислав ©   (03.11.13 22:41) [113]
> Кошмар...

Если я вам и собственно процитировал кусок из комментария к исходнику с некоторыми дополнениями относительно локальных переменных.


 
Владислав ©   (2013-11-03 23:44) [118]

Напомню, что меня интересовало.
При передаче строки в качестве фактического параметра копия строки не создается.

Вы с этим не соглашаетесь и в доказательство своей точки зрения приводите код, в котором копия создается потому, что вы передаете литерал. Чувствуете разницу? Копия создается не потому, что Вы передали строку в качестве параметра, а потому, что Вы присваиваете переменной строку, которая указывает на литерал.
Измените одну строчку кода в Вашем примере 1 из [110]. Вместо:
procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := "1234";
 TMyClass.Create(S);
end;

Напишите, например, такой вариант:
procedure TForm1.FormCreate(Sender: TObject);
var
 s: string;
begin
 s := StringOfChar("1", 10);
 TMyClass.Create(S);
end;

И копия строки уже не будет создаваться.


 
DVM ©   (2013-11-04 00:07) [119]


> Владислав ©   (03.11.13 23:44) [118]
> Напомню, что меня интересовало.
> При передаче строки в качестве фактического параметра копия
> строки не создается.

А я не утверждал обратное. С чего вы это взяли? Я всю дорогу утверждал, что строка скопируется, если ее попробуют изменить, т.е поток будет работать с копией строки (с этого весь сыр бор начался). Правда мне сразу бы следовало расписать всю цепочку умозаключений почему скопируется, а то выходит, что скопируется исключительно из за передачи в качестве параметра. Тут я конечно фигню написал.


> Вы с этим не соглашаетесь и в доказательство своей точки
> зрения приводите код, в котором копия создается потому,
> что вы передаете литерал. Чувствуете разницу? Копия создается
> не потому, что Вы передали строку в качестве параметра,
> а потому, что Вы присваиваете переменной строку, которая
> указывает на литерал.

Это не в доказательство того, что строка копируется, а как доказательство слов DevilDevil, что в данном случае (с литералом) все действительно копируется. Да там и в исходнике написано это, сложно не заметить. Я лишь подтвердил то, что все что он говорит правда.


> И копия строки уже не будет создаваться.

Все верно, не будет.

И все же. Вот эта фраза:

> Вы разобрались, почему у Вас в примере 1 срока скопировалась,
>  а в примере 2 нет?

К чему относилась???
Очевидно, что к моему [110]. И ответом на этот вопрос будет именно то, что я написал в [112] про локальные и глобальные переменные, а совсем не то, что написано в комментарии к коду, который вы привели в [115] - это лишь объяснение того, почему литералы копируются в глобальные переменные, но про локальные там нет ни слова, а в локальные они не копируются.
А вы зачем то написали в ответ Кошмар.


 
Компромисс1 ©   (2013-11-04 18:21) [120]

Кошмар. Senior Delphi developers спорят об основаx работы со string.

String, StringBuilder, StringBuffer и Java вообще rule ;)



Страницы: 1 2 3 4 вся ветка

Форум: "Прочее";
Текущий архив: 2014.05.04;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.72 MB
Время: 0.007 c
15-1383510603
Юрий
2013-11-04 00:30
2014.05.04
С днем рождения ! 4 ноября 2013 понедельник


4-1268893097
DremLIN.Ru
2010-03-18 09:18
2014.05.04
Как достоверно определить что программа запущена из планировщика?


15-1383833853
Ptr_Suspend
2013-11-07 18:17
2014.05.04
Как сделать фунцкию LoWord()?


15-1383253856
Пит
2013-11-01 01:10
2014.05.04
Мышка слишком быстрая


15-1383683403
Юрий
2013-11-06 00:30
2014.05.04
С днем рождения ! 6 ноября 2013 среда





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский