Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Синхронизировать или нет?


Автор: ksili 26.10.2007, 08:05
Вопрос такой. Программа создаёт несколько дочерних потоков. В основном потоке есть глобальная структура (даже массив структур). В каждый поток передается указатель на эту структуру. В процессе работы потоки к ней обращаются.
Надо ли синхронизировать такие обращения? Это же ведь не обращение к VCL... 
У меня пока синхронизированы (запихнуты в Synchronize) только изменения этой структуры из дочерних потоков. Надо ли ещё синхронизировать и чтения?

Автор: Lazin 26.10.2007, 08:35
Через Synchronize нужно синхронизировать только те объекты которые используют очередь сообщений. Все глобальные объекты нужно синхронизировать по другому, например с помощью критических секций (TCriticalSection или API ф-ий). Иногда логика программы требует блокировки других потоков.
например

Код

CRITICAL_SECTION lock;
InitializeCriticalSection(&lock);


код потока

Код

EnterCriticalSection(&lock);
//используем глобальные данные
//одновременно этот участок кода может выполнять только 1 поток, остальные ждут
LeaveCriticalSection(&lock);


Другой пример, если один поток читает std::vector, а другой добавляет элементы в него то может произойти ошибка доступа (если произойдет realloc). В то-же время можно спокойно читать вектор из нескольких потоков.

Код

TMultiReadExclusiveWriteSynchronizer* lock;
std::vector<int> vect;

1 поток - читает
Код

lock->BeginRead();
for (std::vector<int>::iterator i = vect.begin(); i != vect.end(); ++i)
{
 делаем что-то что не меняет контейнер
}
lock->EndRead();


2 поток - добавляет
Код

for (;;)
{
 lock->BeginWrite();
 vect.push_back(somedata);
 lock->EndWrite();
}

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

class Obj
{
bool flag;
public:

Obj() : flag(true) {}

void Method()//этот метод должен выполнить работу только один раз
{
 if (flag)
 {
  Сделать что-нибудь 1 раз
  flag = false;
 }
}
};

Представь что объект Obj используется несколькими потоками и оба вызывают метод Method, произойдет вот что:
оба потока проверят условие примерно в одно время, и flag == true для обоих
проблема решается критической секцией
Код

class Obj
{
bool flag;
CRITICAL_SECTION lock;

public:

Obj() : flag(true) {
  InitializeCriticalSection(&lock);
}

void Method()//этот метод должен выполнить работу только один раз
{
 EnterCriticalSection(&lock);
 if (flag)
 {
  Сделать что-нибудь 1 раз
  flag = false;
 }
 LeaveCriticalSection(&lock);
}
};

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

Автор: ksili 26.10.2007, 08:53
Спасибо за подсказку!
Изменения размера (realloc) у меня к счастью нет.
Цитата(Lazin @  26.10.2007,  13:35 Найти цитируемый пост)
EnterCriticalSection(&lock);
//используем глобальные данные
//одновременно этот участок кода может выполнять только 1 поток, остальные ждут
LeaveCriticalSection(&lock);

У меня нет кода, который может исполняться сразу несколькими потоками. У каждого потока "свои" функции. Даже если есть одинаковые. Но есть общие (глобальные) данные. В этом случае подойдёт ли использование критических секций для безопасного изменения этих данных?
Вот например в основном потоке глобальная структура g_All. В другом потоке есть указатель на неё: common = &g_All. Надо ли делать так:
Код

EnterCriticalSection(&lock);

//используем глобальные данные
common->chnls[i].used = true;

LeaveCriticalSection(&lock);
?

Автор: Lazin 26.10.2007, 09:03
Если структура содержит только POD то можно попробовать вообще без синхронизации, если что-то более сложное, то в структуру нужно добавить объект для синхронизации, и дергать данные не напрямую а через методы get/set.

Автор: ksili 26.10.2007, 09:06
Цитата(Lazin @  26.10.2007,  14:03 Найти цитируемый пост)
в структуру нужно добавить объект для синхронизации, и дергать данные не напрямую а через методы get/set

Где есть про это поподробнее? Или пример?

POD - это имелись в виду просто данные?

P.S. Почему здесь в коде C++Builder выделяется жирным слово lock. У меня в Билдере оно не выделяется...

Автор: Lazin 26.10.2007, 09:51
Цитата(ksili @  26.10.2007,  09:06 Найти цитируемый пост)
POD
 Plain Old Data
Цитата(ksili @  26.10.2007,  09:06 Найти цитируемый пост)
Где есть про это поподробнее? Или пример?
http://www.codeproject.com/threads/Synchronization.asp

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)