ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 15.05.02 11:09
Оценка:
Столкнулся на днях с такой проблемой:

Есть следующий код (немного упрощенный):
//Константная строка
static const CString get_String()
{
  static const CString str = MakeString(); //где MakeString довольно ресурсоемкая функция, 
  // которую не хочется вызывать каждый раз
  return str;
}

//несколько Thread-ов, которые этой строкой пользуются:
void Thread()
{

  for (;;)
  {
    log.AddMessage (get_String());
  }
}


Из-за того, что const CString на самом деле не константый объект (у него модифицируется счетчик ссылок), все падает.

Как быть?

На ум приходит, только одно решение, при возврате строки говорит CString-у, чтобы он делал копию, а не увеличивал счетчик.
Но это тоже не выход, т.к. строка может быть до нескольких килобайтов, а функция дергается довольно часто.

Более общий вопрос, как лучше писать код, которые использует константные объекты из нескольких thread-ов?
Получается, что для таких объектов вообще нельзя пользоваться ссылками, а можно только передавать объекты по значению.
Re: ref_count strings & threads
От: Sergey Россия  
Дата: 15.05.02 12:15
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Столкнулся на днях с такой проблемой:


DG>Есть следующий код (немного упрощенный):

DG>
DG>//Константная строка
DG>static const CString get_String()
DG>{
DG>  static const CString str = MakeString(); //где MakeString довольно ресурсоемкая функция, 
DG>  // которую не хочется вызывать каждый раз
DG>  return str;
DG>}

DG>//несколько Thread-ов, которые этой строкой пользуются:
DG>void Thread()
DG>{

DG>  for (;;)
DG>  {
DG>    log.AddMessage (get_String());
DG>  }
DG>}
DG>


DG>Из-за того, что const CString на самом деле не константый объект (у него модифицируется счетчик ссылок), все падает.


Так смотря как он модифицируется...

DG>Как быть?


Это MFCшный CString или нет? Конкретнее, как у него подсчет ссылок реализован?

DG>На ум приходит, только одно решение, при возврате строки говорит CString-у, чтобы он делал копию, а не увеличивал счетчик.


Или, например, синхронизировать доступ к CString, или вообще возвращать из get_String возвращать const char *.

DG>Но это тоже не выход, т.к. строка может быть до нескольких килобайтов, а функция дергается довольно часто.


DG>Более общий вопрос, как лучше писать код, которые использует константные объекты из нескольких thread-ов?

DG>Получается, что для таких объектов вообще нельзя пользоваться ссылками, а можно только передавать объекты по значению.

Почему? Для настоящих константных объектов?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re: ref_count strings & threads
От: Silver_s Ниоткуда  
Дата: 15.05.02 12:23
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>static const CString get_String()

DG>{
DG> static const CString str = MakeString(); //где MakeString довольно ресурсоемкая функция,
DG> // которую не хочется вызывать каждый раз
DG> return str;
DG>}

DG>//несколько Thread-ов, которые этой строкой пользуются:

DG>void Thread()
DG>{
DG> for (;)
DG> {
DG> log.AddMessage (get_String());
DG> }
DG>}
DG>[/ccode]

А не нужно ли синхронизировать static переменную внутри функции? Ее всетаки ковыряют нескоолько потоков, хоть она и const но если RefCount меняется то может что нехорошее и происходит.

А может это MFC'шные глюки она потоко-капризная не зря же в MSDN такие предложения встречаются:
...
If you have a multithreaded application that creates a thread in a way other than using a CWinThread object, you cannot access other MFC objects from that thread.
...
MFC objects are not thread-safe at the object level, only at the class level
...
As a general rule, a thread can access only MFC objects that it created
Re[2]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 15.05.02 12:33
Оценка:
Здравствуйте Silver_s, Вы писали:

SS>А не нужно ли синхронизировать static переменную внутри функции? Ее всетаки ковыряют нескоолько потоков, хоть она и const но если RefCount меняется то может что нехорошее и происходит.


Ставить Lock на getString() не помогает, т.к. если увеличение счетчика происходит именно в get_String(), то уменьшение счетчика непонятно где.
Re: ref_count strings & threads
От: Аноним  
Дата: 15.05.02 12:43
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Столкнулся на днях с такой проблемой:


DG>Есть следующий код (немного упрощенный):

DG>
DG>//Константная строка
DG>static const CString get_String()
DG>{
DG>  static const CString str = MakeString(); //где MakeString довольно ресурсоемкая функция, 
DG>  // которую не хочется вызывать каждый раз
DG>  return str;
DG>}

DG>//несколько Thread-ов, которые этой строкой пользуются:
DG>void Thread()
DG>{

DG>  for (;;)
DG>  {
DG>    log.AddMessage (get_String());
DG>  }
DG>}
DG>


DG>Из-за того, что const CString на самом деле не константый объект (у него модифицируется счетчик ссылок), все падает.


DG>Как быть?


DG>На ум приходит, только одно решение, при возврате строки говорит CString-у, чтобы он делал копию, а не увеличивал счетчик.

DG>Но это тоже не выход, т.к. строка может быть до нескольких килобайтов, а функция дергается довольно часто.

DG>Более общий вопрос, как лучше писать код, которые использует константные объекты из нескольких thread-ов?

DG>Получается, что для таких объектов вообще нельзя пользоваться ссылками, а можно только передавать объекты по значению.

А зачем возвращать строку по значению? Почему бы не вернуть ссылку на нее?
Re[2]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 15.05.02 12:44
Оценка:
Здравствуйте Sergey, Вы писали:

DG>>Из-за того, что const CString на самом деле не константый объект (у него модифицируется счетчик ссылок), все падает.


S>Так смотря как он модифицируется...


+1 в конструкторе/-1 в деструкторе, простым инкрементом

DG>>Как быть?


S>Это MFCшный CString или нет? Конкретнее, как у него подсчет ссылок реализован?

в данном случае WTL::CString,
InterlockedIncrement\InterlockedDecrement

DG>>На ум приходит, только одно решение, при возврате строки говорит CString-у, чтобы он делал копию, а не увеличивал счетчик.


S>Или, например, синхронизировать доступ к CString, или вообще возвращать из get_String возвращать const char *.


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


DG>>Более общий вопрос, как лучше писать код, которые использует константные объекты из нескольких thread-ов?

DG>>Получается, что для таких объектов вообще нельзя пользоваться ссылками, а можно только передавать объекты по значению.

S>Почему? Для настоящих константных объектов?


А что это такое?

В данном коде, Const — настоящий константный объект или не настоящий?
struct Const
{
  Const():p(new int()){*p = 0;}
  void Increment() const {*p++;}
  void Decrement() const {*p--;}
  ~Const(){delete p;}
  int * p;
};

const Const cnst;

void Thread ()
{
  cnst.Increment(); 
  cnst.Decrement();
}
Re: ref_count strings & threads
От: ppp  
Дата: 15.05.02 12:51
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Столкнулся на днях с такой проблемой:


DG>Есть следующий код (немного упрощенный):


Знаешь, я не поленился и набил у себя аналогичный код — у меня все нормально. Так что ищи ошибку в другом месте.
Если ты такой умный, почему ты такой бедный?
Re[2]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 15.05.02 13:17
Оценка:
Здравствуйте ppp, Вы писали:

ppp>Знаешь, я не поленился и набил у себя аналогичный код — у меня все нормально. Так что ищи ошибку в другом месте.


Так у меня он тоже не каждый раз падает, а один раз на двадцать запусков...
Re[3]: ref_count strings & threads
От: Zero  
Дата: 15.05.02 13:43
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>А что это такое?


DG>В данном коде, Const — настоящий константный объект или не настоящий?

DG>
DG>struct Const
DG>{
DG>  Const():p(new int()){*p = 0;}
DG>  void Increment() const {*p++;}
DG>  void Decrement() const {*p--;}
DG>  ~Const(){delete p;}
DG>  int * p;
DG>};

DG>const Const cnst;

DG>void Thread ()
DG>{
DG>  cnst.Increment(); 
DG>  cnst.Decrement();
DG>}

DG>


А почему он должен быть ненастоящим? Кончено, настоящий.
Re[3]: ref_count strings & threads
От: Sergey Россия  
Дата: 15.05.02 13:48
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте Sergey, Вы писали:


DG>>>Из-за того, что const CString на самом деле не константый объект (у него модифицируется счетчик ссылок), все падает.


S>>Так смотря как он модифицируется...


DG>+1 в конструкторе/-1 в деструкторе, простым инкрементом


Где там простой инкремент/декремент? Какая версия WTL?

S>>Это MFCшный CString или нет? Конкретнее, как у него подсчет ссылок реализован?

DG>в данном случае WTL::CString,
DG>InterlockedIncrement\InterlockedDecrement

Тогда с подсчетом ссылок проблем быть не должно, скорее всего либо используешь неконстантные касты, либо где-то в реализации CString есть другая ошибка.

DG>>>На ум приходит, только одно решение, при возврате строки говорит CString-у, чтобы он делал копию, а не увеличивал счетчик.


S>>Или, например, синхронизировать доступ к CString, или вообще возвращать из get_String возвращать const char *.


DG>Синхроyизировать доступ не получается, т.к. уменьшение счетчика происходит не понятно где...


Типа так:
void Thread()
{

for (;)
{
EnterCriticalSection(..);
log.AddMessage (get_String());
LeaveCriticalSection(..);
}
}

DG>>>Более общий вопрос, как лучше писать код, которые использует константные объекты из нескольких thread-ов?

DG>>>Получается, что для таких объектов вообще нельзя пользоваться ссылками, а можно только передавать объекты по значению.

S>>Почему? Для настоящих константных объектов?


DG>А что это такое?


DG>В данном коде, Const — настоящий константный объект или не настоящий?

DG>
DG>struct Const
DG>{
DG>  Const():p(new int()){*p = 0;}
DG>  void Increment() const {*p++;}
DG>  void Decrement() const {*p--;}
DG>  ~Const(){delete p;}
DG>  int * p;
DG>};

DG>const Const cnst;

DG>void Thread ()
DG>{
DG>  cnst.Increment(); 
DG>  cnst.Decrement();
DG>}

DG>


А это вообще не должно компилироваться, насколько я понимаю. А если напишешь mutable или const_cast — то не настоящий
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 15.05.02 13:58
Оценка:
Здравствуйте Sergey, Вы писали:


DG>>В данном коде, Const — настоящий константный объект или не настоящий?

DG>>
DG>>struct Const
DG>>{
DG>>  Const():p(new int()){*p = 0;}
DG>>  void Increment() const {*p++;}
DG>>  void Decrement() const {*p--;}
DG>>  ~Const(){delete p;}
DG>>  int * p;
DG>>};

DG>>const Const cnst;

DG>>void Thread ()
DG>>{
DG>>  cnst.Increment(); 
DG>>  cnst.Decrement();
DG>>}

DG>>


S>А это вообще не должно компилироваться, насколько я понимаю.

А ты пробовал компилировать? '*p', конечно, должно стоять в скобках
DG>>  void Increment() const {(*p)++;}
DG>>  void Decrement() const {(*p)--;}


S>А если напишешь mutable или const_cast — то не настоящий
Re[4]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 15.05.02 14:11
Оценка:
Здравствуйте Sergey, Вы писали:


S>Типа так:

S>void Thread()
S>{

S> for (;)

S> {
S> EnterCriticalSection(..);
S> log.AddMessage (get_String());
S> LeaveCriticalSection(..);
S> }
S>}

Так деструктор для CString'а все равно может вызваться вне блокировки.

CLog
{
  CString s;
  CComAutoCriticalSection cs;
  AddMessage (const CString&s)
  {
    CAutoLock lock(cs);
    this->s = s;
  }
  CString get_Message ()
  { 
    CAutoLock lock(cs);
    return s;
  }
};

void Thread ()
{
  for (;;)
  {
    EnterCriticalSection(..); 
    log.AddMessage (get_String()); 
    LeaveCriticalSection(..); 
  }
}

void AnotherThread()
{
  for (;;)
  {
    CString s = log.get_Message();
    //здесь вызовется деструктор все того же "static стринга", но он будет уже без всякого локинга
  }
}
Re[5]: ref_count strings & threads
От: Sergey Россия  
Дата: 15.05.02 14:23
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте Sergey, Вы писали:


S>>А это вообще не должно компилироваться, насколько я понимаю.

DG>А ты пробовал компилировать? '*p', конечно, должно стоять в скобках

Со скобками, естественно, скомпилируется. Потому что то, куда показывает p, к объекту никаким боком не относится.

DG>
DG>>>  void Increment() const {(*p)++;}
DG>>>  void Decrement() const {(*p)--;}
DG>


S>>А если напишешь mutable или const_cast — то не настоящий
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[5]: ref_count strings & threads
От: Sergey Россия  
Дата: 15.05.02 14:45
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте Sergey, Вы писали:



S>>Типа так:

S>>void Thread()
S>>{

S>> for (;)

S>> {
S>> EnterCriticalSection(..);
S>> log.AddMessage (get_String());
S>> LeaveCriticalSection(..);
S>> }
S>>}

DG>Так деструктор для CString'а все равно может вызваться вне блокировки.


DG>
DG>CLog
DG>{
DG>  CString s;
DG>  CComAutoCriticalSection cs;
DG>  AddMessage (const CString&s)
DG>  {
DG>    CAutoLock lock(cs);
DG>    this->s = s;
DG>  }
DG>  CString get_Message ()
DG>  { 
DG>    CAutoLock lock(cs);
DG>    return s;
DG>  }
DG>};

DG>void Thread ()
DG>{
DG>  for (;;)
DG>  {
DG>    EnterCriticalSection(..); 
DG>    log.AddMessage (get_String()); 
DG>    LeaveCriticalSection(..); 
DG>  }
DG>}

DG>void AnotherThread()
DG>{
DG>  for (;;)
DG>  {
DG>    CString s = log.get_Message();
DG>    //здесь вызовется деструктор все того же "static стринга", но он будет уже без всякого локинга
DG>  }
DG>}

DG>


А что мешает написать так?

void AnotherThread()
{
  for (;;)
  {
    EnterCriticalSection(..);
    CString s = log.get_Message();
    LeaveCriticalSection(..); 
  }
}


А вообще, IMHO, проблемы там с чем угодно, только не с подсчетом ссылок. Например, если один поток начинает модифицировать CString, который вернул get_Message, а другой — менять CLog::s.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[6]: ref_count strings & threads
От: Sergey Россия  
Дата: 15.05.02 14:54
Оценка:
S>А что мешает написать так?

Тьфу, вот так, естественно:

void AnotherThread()
{
  for (;;)
  {
    EnterCriticalSection(..);
    { CString s = log.get_Message(); }
    LeaveCriticalSection(..); 
  }
}


S>А вообще, IMHO, проблемы там с чем угодно, только не с подсчетом ссылок. Например, если один поток начинает модифицировать CString, который вернул get_Message, а другой — менять CLog::s.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: ref_count strings & threads
От: ppp  
Дата: 15.05.02 20:03
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте ppp, Вы писали:


ppp>>Знаешь, я не поленился и набил у себя аналогичный код — у меня все нормально. Так что ищи ошибку в другом месте.


DG>Так у меня он тоже не каждый раз падает, а один раз на двадцать запусков...


так не считается... Либо всегда падает в процессе работы, либо у тебя где-то в другом месте крашится память

на посмотри мой исходник и скажи, где я неправ

#include <process.h>
#include <afx.h>
#include <windows.h>

typedef unsigned (__stdcall *THREAD) (void *);
typedef HANDLE cyThreadID;
cyThreadID CyStartThread(THREAD threadName, void *pParam=NULL);

cyThreadID CyStartThread(THREAD threadName, void *pParam)
{
  unsigned threadID;
  return (cyThreadID)_beginthreadex(NULL, 4096, threadName, pParam, 0, &threadID);
}

THREAD myThread(void *pParam);

static const CString get_String()
{
  static const CString qq("aaaabbbbb");
  return qq;
}

int main()
{
  CyStartThread((THREAD)myThread, NULL);
  CyStartThread((THREAD)myThread, NULL);
  CyStartThread((THREAD)myThread, NULL);
  CyStartThread((THREAD)myThread, NULL);


  while( true )
  {
    Sleep(1000);
  }

  return 0;
}

THREAD myThread(void *pParam)
{
  for (;;)
  {
    printf("%s\n", get_String());
    CString aaa = get_String();
  }
}
Если ты такой умный, почему ты такой бедный?
Re[4]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 16.05.02 09:11
Оценка:
Здравствуйте ppp, Вы писали:

ppp>так не считается... Либо всегда падает в процессе работы, либо у тебя где-то в другом месте крашится память


Или иногда одновременно что-то правят.


Если твой код чуть-чуть подправить, то он начинает падать
#include <process.h>
#include <afx.h>
#include <windows.h>

typedef unsigned (__stdcall *THREAD) (void *);
typedef HANDLE cyThreadID;
cyThreadID CyStartThread(THREAD threadName, void *pParam=NULL);

cyThreadID CyStartThread(THREAD threadName, void *pParam)
{
  unsigned threadID;
  return (cyThreadID)_beginthreadex(NULL, 4096, threadName, pParam, 0, &threadID);
}

THREAD myThread(void *pParam);


static CString MakeString()
{
  Sleep (100);
  return "aabba";
};

static const CString get_String()
{
  static const CString qq = MakeString();
  return qq;
}

int main()
{
  CyStartThread((THREAD)myThread, NULL);
  CyStartThread((THREAD)myThread, NULL);
  CyStartThread((THREAD)myThread, NULL);
  CyStartThread((THREAD)myThread, NULL);


  while( true )
  {
    Sleep(1000);
  }

  return 0;
}

THREAD myThread(void *pParam)
{
  for (;;)
  {
    printf("%s\n", get_String());
    CString aaa = get_String();
  }
}
Re[5]: ref_count strings & threads
От: ppp  
Дата: 16.05.02 09:35
Оценка:
Здравствуйте DarkGray, Вы писали:

DG>Здравствуйте ppp, Вы писали:


ppp>>так не считается... Либо всегда падает в процессе работы, либо у тебя где-то в другом месте крашится память


DG>Или иногда одновременно что-то правят.



DG>Если твой код чуть-чуть подправить, то он начинает падать

DG>

DG>
DG>static CString MakeString()
DG>{
DG>  Sleep (100);
DG>  return "aabba";
DG>};
DG>
DG>static const CString get_String()
DG>{
DG>  static const CString qq = MakeString();
DG>  return qq;
DG>}

DG>


Ну кто тебе доктор
Если это у тебя действительно константная строка, то вычисляй ее перед запуском потоков.

int main()
{
  get_String();
  CyStartThread((THREAD)myThread, NULL);
.........................................
}
Если ты такой умный, почему ты такой бедный?
Re[6]: ref_count strings & threads
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 16.05.02 09:46
Оценка:
Здравствуйте ppp, Вы писали:


ppp>Ну кто тебе доктор

ppp>Если это у тебя действительно константная строка, то вычисляй ее перед запуском потоков.

Это я уже сделал, но у там похоже еще куча подводных камней.

//вот такой код, тоже, небось может падать
static const CString s = MakeString();

void Thread ()
{
  CString s2 = s;
  s2 += "QQ";
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.