Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> ...Эх потоки... пыль, да туман... холода, тревоги да степной бурьян... 
V
    Опции темы
Paspartu
Дата 22.6.2010, 23:54 (ссылка)    | (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 67
Регистрация: 3.5.2007

Репутация: нет
Всего: нет



Доброго времени суток! 
…Вот, разбираюсь с потоками, прочитал Дж. Рихтера, но возникли вопросы по синхронизации
 и вообще в каких случаях ее делать, так что вопрос чисто теоретический:

Допустим есть функция : (.cpp):
Код

namespace
{
    // Куча разных вспомогательных функций
}

bool IsPrime (INT64 n)
{
    // пользуемся вспомогательными функциями возвращаем результат
}


И где-то класс CBaseThread который создает поток со static методом:
Код

//…
unsigned WINAPI CBaseThread::ThreadEntry(void *pArg)
{
    CBaseThread* pActive = (CBaseThread*) pArg;
    // Здесь pActive – указатель на свойже класс

    pActive->Run(); // вызов метода Run() в наследнике CBaseThread
    // Run – чисто виртуальный…
    
    return 0;
}
//…



// Где-то в производном классе:
Код

void CDerived::Run()
{
       // Сюда попадаем из каждого потока т.е. из каждого экземпляра класса
       // CBaseThread (CDerived), хорошо, но!
       //
       // 1. Могу ли я вызвать прямо отсюда IsPrime() ? или необходимо ее вызов 
       // синхранизировать, т.к. несколько потоков будут пытаться в нее
       // залезть?
       //
       // 2. Если синхронизировать доступ, то только один поток сможет в нее
       // залезть, а если есть необходимость в одновремменной работе – нужны
       // несколько экземляров?
       //
       // 3. Если необходимо несколько экземляров – это класс т.е. IsPrime()
       // поместить в класс, напрашивается этот же,но необходимо в другой (так 
       // надо), пусть в класс… СPrime.
       // Есть ли разница как создавать этот класс на стеке или new если он
       // будет являться членом CDerived (в случае new – хранить указатель)? 
       // 
       // 4. И вообще что происходит если разные потоки пытаются залезть в один
       // и тот же класс через один указатель? Если доступ к членам класса,
       // вроде понятно, что надо в любом случае синхронизировать на
       // чтение/запись так ли это? …А что происходит к аналогичному доступу,
       // но к методам класса? 

}




PM MAIL   Вверх
xvr
Дата 23.6.2010, 00:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(Paspartu @  22.6.2010,  23:54 Найти цитируемый пост)
1. Могу ли я вызвать прямо отсюда IsPrime() ? или необходимо ее вызов 
       // синхранизировать, т.к. несколько потоков будут пытаться в нее
       // залезть?
Зависит от содержимого функции IsPrime. Если она (прямо или косвенно, через вызываемые из нее функции) не модифицирует глобальные объекты - то можно звать как есть.
Если что то модифицирует - то надо смотреть, что и как (в общем случае нужно синхронизировать)


Цитата(Paspartu @  22.6.2010,  23:54 Найти цитируемый пост)
       // 2. Если синхронизировать доступ, то только один поток сможет в нее
       // залезть, а если есть необходимость в одновремменной работе – нужны
       // несколько экземляров?
Опять же - если необходимо что бы разные потоки видели общие данные, модифицируемые этой функцией - то нужна синхронизация. Если же вызовы из разных потоков не влияют друг на друга - то можно сделать несколько экземпляров (но не функции, а данных, с которыми она работает)

Цитата

3. Если необходимо несколько экземляров – это класс т.е. IsPrime()
       // поместить в класс, напрашивается этот же,но необходимо в другой (так 
       // надо), пусть в класс… СPrime.
       // Есть ли разница как создавать этот класс на стеке или new если он
       // будет являться членом CDerived (в случае new – хранить указатель)? 
С точки зрения многопоточности разницы нет. С точки зрения здравого смысла со стеком нужно обращаться осторожно - он не резиновый (особенно в потоках)

Цитата(Paspartu @  22.6.2010,  23:54 Найти цитируемый пост)
А что происходит к аналогичному доступу,
       // но к методам класса? 
Ничего. Защищать нужно в конечном случае именно данные. Сериализация вызовов методов делается ИМЕННО для защиты данных, с которыми они работают


PM MAIL   Вверх
Earnest
Дата 23.6.2010, 07:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Цитата(xvr @  23.6.2010,  01:07 Найти цитируемый пост)
Зависит от содержимого функции IsPrime. Если она (прямо или косвенно, через вызываемые из нее функции) не модифицирует глобальные объекты - то можно звать как есть.

Не совсем так. Если IsPrime читает переменные, которые могут изменять другие функции (конкурентно), то тоже нужно защищать. Даже простые переменные типа int. И вообще, пока не встали вопросы производительности, я бы в начале всех функций, которые могут вызываться из разных потоков, поставила блокировку.


--------------------
...
PM   Вверх
jonie
Дата 23.6.2010, 07:28 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 7
Всего: 118



Earnest, чтение потокобезопасно. Лично я не вижу смысла ставить блокировку на чтение - на запись ставить надо. Паттерн UnitOfWork в общем с блокировкой типа "заблокировал и всё че надо поменял одним махом".

В .NET например нет функций чтения Interlocked* - именно потому что всегда прочтенное значение будет именно то которое надо, как бы логически это не смотрелось неверным - просто один поток успеть может прочесть до изменения - но бловировка тут ничего не даст - он все-равно прочтет "старое" значение.


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
GremlinProg
Дата 23.6.2010, 08:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



а Interlocked* и введены как раз для изменения значения, читать - понятно можно и без синхронизации,
но вот если к примеру идет очень точный счет среди потоков, и они для этого счета используют общую переменную,
которая постоянно меняется этими же потоками, то простое чтение без синхронизации может привести к неожиданным результатам:

переменная еще не дописана потоком 1, а поток 2 уже начал ее читать, причем, когда поток 2 закончил ее читать, полток 1 только закончил в нее писать 

что получится?
поток 2 может не получить не только нового значения переменной, но и не старого, т.е. поток 2 в момент расчета следующей итерации точно не опирается на расчеты потока 1

в таких случаях синхронизация нужна даже на чтение,
да это не только атомарной переменной касается,

вот например идет заполнение массива потоком 1, поток 2 может его спокойно читать без синхронизации,
но что он в итоге прочитает, если для потока 2 массив актуален только в заполненном состоянии?


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
xvr
Дата 23.6.2010, 09:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(jonie @ 23.6.2010,  07:28)
Earnest, чтение потокобезопасно. 

Но может быть не атомарно, в таком случае не гарантируется целостность считанного значения

PM MAIL   Вверх
jonie
Дата 23.6.2010, 10:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 7
Всего: 118



GremlinProgxvr, читать можно без локов, всегда. Никакая атомарность чтения не проблема, т.к. запись атомарна (вот где локи нужны) - остальные ждут пока все запишется.
Цитата

переменная еще не дописана потоком 1, а поток 2 уже начал ее читать, причем, когда поток 2 закончил ее читать, полток 1 только закончил в нее писать 
так не может быть - при записи мы залочим других - они просто будут ждать пока все запишется - критическая секция в общем. Записывающий поток "отпустит" когда произведет запись.

Чета я в общем сомневаюсь что вышеописанная неатомарность чтения на intel совместимых процах реализуема при должном обеспечении блокировки при записи

Добавлено @ 11:00
Цитата

вот например идет заполнение массива потоком 1, поток 2 может его спокойно читать без синхронизации,
но что он в итоге прочитает, если для потока 2 массив актуален только в заполненном состоянии? 
это уже дизайн такой, потоки тут непричем. Даже в одномпотоке, заменив слово "поток" на "функция" получим тот же "косяк". Тут надо завести bool bFilled и синхронизироваться при ее записи, а второй поток пусть ожидает в while(!bFilled) без синхронизации.

В общем не надо путать проблемы дизайна решения и проблемы потоков.

Это сообщение отредактировал(а) jonie - 23.6.2010, 11:03


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
GremlinProg
Дата 23.6.2010, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(jonie @  23.6.2010,  12:58 Найти цитируемый пост)
GremlinProg, xvr, читать можно без локов, всегда. Никакая атомарность чтения не проблема, т.к. запись атомарна (вот где локи нужны) - остальные ждут пока все запишется.

это все правильно до тех пор, пока процессор и/или ядро в единственном экземпляре, т.е. в полном отсутствии параллелизма

Добавлено через 1 минуту и 11 секунд
в наше время это уже редкость


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Paspartu
Дата 23.6.2010, 11:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 67
Регистрация: 3.5.2007

Репутация: нет
Всего: нет



Доброго времени суток!
Всем огромное спасибо за разъяснение, но поправьте меня если я ошибаюсь:
1. Синхронизацию нужно проводить только при доступе различных потоков к общим данным
Таким образом: 

a). Если 1-й поток читает к примеру массив то если в этот момент 2-й поток пытается его изменить то его нужно блокировать, пока чтение не будет завершено.
б). Если 1-й пишет, а 2-й читает то нужна блокировка 2-го потока так что бы он читал уже измененные значения. Без нее ничего страшного не произойдет? Кроме того, что будет прочитано старое значение?
в). Если оба потока читают – блокировка какого-либо из них не нужна.

2. Если происходит вызов (одной и той же) функции которая возвращает значение сколько угодными потоками то синхронизация не нужна так как общих данных в ней нет, а возвращаемое значение, ее параметры и ее локальные переменные будут для каждого потока своими?

3. Запутался с классами и их методами… если класс создан через new и в каждом потоке используется указатель на него, что будет копироваться в стек потока? К примеру, в разных потоках через указатель будет вызываться один и тот же метод, аналогично будут копироваться только возвращаемое значение, параметры метода и его локальные переменные? Или повторюсь, есть ли необходимость для каждого потока создавать свой экземпляр класса?
т.е. есть класс:

Код

class CA
{
public:
    // ...
    void        F1(int a, int b);
    double    F2(int a, int b, int c);
        int        F2(int a) const;
    // ...
private:
    int        m_n;
    double    m_db;
    char     m_ch;
    // ...
};



Если при вызове методов F1, F2 их параметры будут копироваться в стек потока, то что будет с его членами m_n, m_db, m_ch? Если мы используем оди и тот же указатель на этот класс в разных потоках. В каких случаях нужна синхронизация методов F1, F2? Только если они изменяют значения членов класса? Нужно ли синхронизировать const или нет, т.к. он не изменяет объект? Опять же таки если в классе нет общих данных нужных нескольким потокам, к примеру в нем несколько методов, а члены int m_n и т.д. испоьзуются как промеж. Значения вычислений(образно)… можно ли при исопользовании одного экземляра для разных потоков обойтись без синхронизации, или она небходима, т.к. члены int m_n и т.д. все таки будут общими и они не будут копироваться в стек? 
Короче говоря… как бы с функциями вроде понятно, что они (значения локальных переменых и т.д.) копируются в стек, а если используется общий указатель на класс? Копирование в стек потока параметров и локальных переменных метода происходит в момент вызова потоком данного метода? А что происходит с членами класса (не методами) они становяться общими? А если мы в каждом потоке используем свой экземпляр то, синхронизация не нужна т.к. общих данных нет?

… Много налил воды… но все же помогите разобраться раз и навсегда… наверное. smile 

PM MAIL   Вверх
GremlinProg
Дата 23.6.2010, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(jonie @  23.6.2010,  12:58 Найти цитируемый пост)
Даже в одномпотоке, заменив слово "поток" на "функция" получим тот же "косяк"

не-не, в одном потоке такого эффекта не получишь


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
xvr
Дата 23.6.2010, 12:11 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(jonie @  23.6.2010,  10:58 Найти цитируемый пост)
GremlinProg, xvr, читать можно без локов, всегда. Никакая атомарность чтения не проблема, т.к. запись атомарна (вот где локи нужны) - остальные ждут пока все запишется.
Проблема, даже с атомарной записью. Пример - читаем 64х битное значение на 32х битном процессоре. Чтение будет производится в 2 приема (2 по 32 бита). Другой процесс в это время инвертирует значение переменной. Предположим, что в начале переменная равна 0, чтение начинается с младшего слова, и 2й процесс записал новое значение между 2мя чтениями 1го процесса. В результате 1й процесс прочтет 0xFFFFFFFF00000000, что неверно с любой точки зрения  smile 


PM MAIL   Вверх
xvr
Дата 23.6.2010, 12:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(Paspartu @  23.6.2010,  11:06 Найти цитируемый пост)
a). Если 1-й поток читает к примеру массив то если в этот момент 2-й поток пытается его изменить то его нужно блокировать, пока чтение не будет завершено.
б). Если 1-й пишет, а 2-й читает то нужна блокировка 2-го потока так что бы он читал уже измененные значения. Без нее ничего страшного не произойдет? Кроме того, что будет прочитано старое значение?
в). Если оба потока читают – блокировка какого-либо из них не нужна.
Если ЛЮБОЙ поток модифицирует содержимое массива, то нужен какой либо способ СИНХРОНИЗАЦИИ доступа к массиву. Эта синхронизация должна в том или ином виде быть применена ко ВСЕМ потокам, которые работают с массивом, т.е. это по сути принадлежность массива, а не потока.
В случае если только 1 поток пишет данные, а все остальные читают, синхронизация может быть сильно упрощена (вплоть до полного отсуствия, если изменяемые данные имеют длинну 32 бита и выровненны на 4х байтовую границу)
Может сильно помочь набор Interlocked* функций (уже упоминались). С ними можно избежать блокировки потоков

Цитата(Paspartu @  23.6.2010,  11:06 Найти цитируемый пост)
2. Если происходит вызов (одной и той же) функции которая возвращает значение сколько угодными потоками то синхронизация не нужна так как общих данных в ней нет, а возвращаемое значение, ее параметры и ее локальные переменные будут для каждого потока своими?
Да

Цитата(Paspartu @  23.6.2010,  11:06 Найти цитируемый пост)
К примеру, в разных потоках через указатель будет вызываться один и тот же метод, аналогично будут копироваться только возвращаемое значение, параметры метода и его локальные переменные?
Да. Но вот члены класса (переменные) будут общими

Цитата(Paspartu @  23.6.2010,  11:06 Найти цитируемый пост)
Или повторюсь, есть ли необходимость для каждого потока создавать свой экземпляр класса?
Это сильно зависит от предназначения класса

Цитата(Paspartu @  23.6.2010,  11:06 Найти цитируемый пост)
Если при вызове методов F1, F2 их параметры будут копироваться в стек потока, то что будет с его членами m_n, m_db, m_ch?
Будут общими

Члены классов (переменные) ничем не отличаются (для multithread'а) от обычных глобальных переменных. Все требования по сериализации доступа так же применимы и к ним

NB. Обычно при разделении одной переменной между потоками сериализацию применяют ИМЕННО к переменной, а не к потокам. Пример:
Код

void some_action(int some)
{
 EnterCriticalSection(&cs);
 my_global_var+=some;
 LeaveCriticalSection(&cs);
}
Эту функцию можно безопасно вызывать из любых потоков - она сама сериализует доступ к глобальной переменной my_global_var

Можно написать С++ класс (указатель), который будет сериализовать доступ к объекту, который он содержит
Код

template<class Item>
class TSafePtr {
 Item* self;
 CRITICAL_SECTION cs;
public:
 TSafePtr(Item* s) :self(s)
  {
   InitializeCriticalSection(&cs);
  }
 ~TSafePtr()
  {
   DestroyCriticalSection(&cs);
  }

 class Proxy {
   Item* self;
   CRITICAL_SECTION& cs;
 public:
   Proxy(Item* s, CRITICAL_SECTION& c) : self(s), cs(c)
    {
     EnterCriticalSection(&cs);
    }
  ~Proxy()
   {
     LeaveCriticalSection(&cs);
   }
  Item* operator -> () {return self;}
 };

 Proxy operator -> () {return Proxy(self,cs);}
};

(Не компилировал, писал прямо тут)
Использование:
Код

MyClass mclass;

TSafePtr<MyClass> my_ptr(&mclass);

my_ptr->some();

Вызов MyClass::some(); будет сериализован



PM MAIL   Вверх
Paspartu
Дата 23.6.2010, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 67
Регистрация: 3.5.2007

Репутация: нет
Всего: нет



Конечно, про 32-х и 64-х битные процессоры... это, конечно, все интересно  smile , но нельзя ли как это там... ближе к теме, а уж лучше конкретно по вопросам...  smile 

PM MAIL   Вверх
jonie
Дата 23.6.2010, 12:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 7
Всего: 118



xvr, хм, ладно убедили)


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
Paspartu
Дата 23.6.2010, 12:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 67
Регистрация: 3.5.2007

Репутация: нет
Всего: нет



Ура!!!  smile 
xvr, С-П-А-С-И-Б-О !!!

К сожалению, у меня не достаточно прав, на увеличение Вашего рейтинга, и все что могу - это еще раз спасибо за разъясгегие!!!  smile

Добавлено позже
Ура!!!  smile 
xvr, С-П-А-С-И-Б-О !!!

К сожалению, у меня не достаточно прав, на увеличение Вашего рейтинга, и все что могу - это еще раз спасибо за разъясгегие!!!  smile

Добавлено через 3 минуты и 28 секунд
Еще раз всем  спасибо! Многое для меня прояснилось!
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0965 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.