Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: вопрос по потокам  (Прочитано 21145 раз)
0 Пользователей и 1 Гость смотрят эту тему.
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« : 30-09-2004 07:39 » 

интересует механизм работы с глобальными переменными, будь то классы (типа TList), будь-то "классические" глобальные переменные, в частности, динамические массивы и массивы вариантов.

надо ли синхронизировать потоки при работе с такими переменными? что будет, если два потока одновременно изменяют одну и ту же переменную (которая может быть достаточно большой, массив с десятками тысяч записей, по сути - загруженая в память таблица базы данных)?

и ещё вопрос. как правильно организовать работу нескольких потоков с com-портом (чтение/запись)), будут ли там какие-либо грабли?
Записан

Серж
Гость
« Ответ #1 : 30-09-2004 08:04 » 

x77, по-моему, подходят все стандартные механизмы синхронизации:
критические секции, мутексы, семафоры, события, главное, чтобы потоки не писали одновременно, а что касается com-порта, то если все потоки читают в общий буфер и складывают в общий буфер для отправки, то тоже проблем не должно быть. Конечно, указатели буферов также должны быть глобальными переменными, а операции чтения-записи надо разделить по времени критическими секциями и т.п.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 30-09-2004 08:30 » 

x77, самое простое - глобальный флаг. Если поток видит, что флаг установлен - работать с переменной низя. А сброшен - поток быстренько устанавливает флаг и работает. Потом сбрасывает

имхо
Записан

x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #3 : 30-09-2004 08:47 » 

Алексей1153,  я думал об этом. трабла в том, что может получиться так, что один поток всё время будет сбрасывать и ставить этот флаг, блокируя работу других потоков. и здесь остаются два варианта: общий буфер на чтение/запись, как предлагает Серж, или очередь потоков: глобальная переменная, массив, в котором потоки просто тупо засовывают в конец свой ThreadId. работает только тот поток, который находится в списке первым. отработав, он удаляет себя из списка, очередь "сдвигается". так можно гарантировать, что каждый поток отработает своё именно в порядке возникновения необходимости отработать.

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

Серж
Гость
« Ответ #4 : 30-09-2004 09:03 » 

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

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #5 : 30-09-2004 09:51 » 

Серж, можно подробнее про асинхронные чтение/запись? я просто делал CeateFile/OpenFile и дальше работал с хэндлом, как с обычным файловым дескриптором. насколько я понимаю, асинхронностью здесь не пахнет?
Записан

Серж
Гость
« Ответ #6 : 30-09-2004 10:17 » 

x77, функцию CreateFile нужно вызвать, добавив в предпоследний параметр флаг FILE_FLAG_OVERLAPPED. Затем нужно создать две структуры типа OVERLAPPED для чтения и записи. В этих структурах содержатся эвенты, сигнализирующие об окончании операции ввода-вывода. Функции ReadFile и WriteFile запускаются с указанием адресов этих структур в последнем параметре. После вызова управление мгновенно передается следующему оператору программы. В отдельных потоках с помощью функций WaitForSingleObject или WaitForMultipleObjects и т.п. отслеживаются эвенты из структур OVERLAPPED.
Записан
Серж
Гость
« Ответ #7 : 30-09-2004 10:25 » 

x77, забыл еще добавить, что события в OVERLAPPED нужно создать с помощью CreateEvent.
Записан
Серж
Гость
« Ответ #8 : 30-09-2004 10:42 » 

x77, еще вдогонку, сразу не нашел ссылку. В MSDN есть статья, которая называется "Serial Communications in Win32", автор Allen Denver, там подробно описывается этот механизм применительно к com-порту.
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #9 : 30-09-2004 10:50 » 

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

Серж
Гость
« Ответ #10 : 30-09-2004 11:13 » 

x77, да, конечно, нужно хэндлы создать в основной программе и либо сделать их глобальными, либо передать каким-нибудь другим способом в каждый поток.
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #11 : 30-09-2004 11:50 » 

всем спасибо, бум пробовать.
Записан

KiborK
Гость
« Ответ #12 : 01-10-2004 18:52 » 

А как вам такая идея:
допустим есть процес А и процес Б которые будут писать и читать данные в одну переменную... Ну так разрешу себе перефразировать задачу: Есть устройство ввода вывода и два приложения ктоторым нужен доступ к ним. Ну так какое стандарное решение, создаем процес В который будет драйвером а интерфейс реализуем через WinAPI. тоесть хандле в процессе драйвере получает сообщение на запрос о чтении\заиси и возврат информацыи через сообщение на нужный хандле. Так получается что недопустимые функцыи процессы выполняют по порядку о все остальное одновременно
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #13 : 01-10-2004 19:21 » 

KiborK, согласен, но это усложнённый вариант 2: по сути, та же очерёдность потоков. смысл многопоточности теряется, т.к. в каждый отдельно взятый момент с устройством будет работать только один поток. а будет он выделенным специально для этого, или будет действовать принцип очерёдности - это уже дело техники.

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

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #14 : 02-10-2004 07:05 » 

x77, а чем мутексы не устраивают, особенно если потоки разные?
это по сути те же флаги...

Цитата
но меня в этом смущает другое: по сути это всё механизмы превратить многопоточные приложения в одно-поточное, китайский коммунизм. какой смысл связываться с несколькими потоками, если в конечном итоге всё равно в данный момент времени будет отрабатывать только один. выходит, что работать с комами и глобальными переменными именно в потоках, т.е. одновременно, нельзя? так может тогда проще и не связываться с ними вообще?

ну так ОДИН поток (суть главный процесс) по определению не может сразу с двух сторон к переменной подбираться! В одном потоке гарантированно не будет накладок

 :arrow:
кстати, пример с мутексами (на си, правда, но думаю - и так понятно) :

Код:
#include "AFXMT.H"

UINT thread1(LPVOID p)
{
  //создаём мутекс с именем stop
  CMutex m(0,"stop");

   //на этом участке коданадо синхронизировать
   m.Lock(INFINITE);//блокировка(на бесконечное время)
   ...
   ...
   m.Unlock();//участок завершён, разблокировка

   //некритичный участок кода
}
Код:
UINT thread2(LPVOID p)
{
  //создаём мутекс тоже с именем stop
  CMutex m(0,"stop");

   //на этом участке коданадо синхронизировать
   m.Lock(INFINITE);//блокировка(на бесконечное время)
   ...
   ...
   m.Unlock();//участок завершён, разблокировка

   //некритичный участок кода
}

то есть мутекс "stop" управляет доступам ко всем критическим кускам кода, которые блокированы вызовом его Lock. Если при вызове Lock како-либо поток уже имеет доступ к участку , то новый поток блокируется, пока тот, более ранний поток, не освободит by Unlock(). И наоборот.

 :arrow:
кстати, у Lock() такой прототип:
virtual BOOL Lock(DWORD dwTimeout = INFINITE);

то есть можно задать бесконечное время блокировки (что будет и по умолча), а можно задать конкретное время (полагаю - в мс). И по истечению времени Lock() вернёт False, если поток так и не получил доступ.

 :arrow:
Цитата
трабла в том, что может получиться так, что один поток всё время будет сбрасывать и ставить этот флаг, блокируя работу других потоков.

так он же всё равно должен давать время другим потокам Улыбаюсь иначе - какая же энто нафиг многозадачность! На крайняк - вставь в этот поток маленький тормоз типа

for(UINT i =100;i;i--);
« Последнее редактирование: 01-12-2007 16:38 от Алексей1153++ » Записан

x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #15 : 04-10-2004 08:50 » 

Лёха, с мьютексами есть сурьёзная грабля. если ты создал мьютекс, заблокировал и прога вылетела - тебе придётся перезнагружать комп. в моём случае - сервер, отвечающий за систему безопасности. это не катит ни под каким видом, требования к отказоустойчивости - на первом месте.
Записан

Серж
Гость
« Ответ #16 : 04-10-2004 10:56 » 

x77, всюду, где программа может вылететь, например, при работе с файлами, нужно обрабатывать исключения( try finally)  и по finally освобождать мьютексы.
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #17 : 04-10-2004 11:40 » 

Серж, это не гарантирует освобождения мьютекса. представь, что программу просто выключили. i.e. - сняли процесс. я не уверен, что finally отработает.
Записан

Серж
Гость
« Ответ #18 : 04-10-2004 12:07 » 

x77, если программу сняли, то убьются все потоки, ею запущенные,
думаю закроются и мьютексы, в худшем случае какой-нибудь мусор останется в памяти.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #19 : 04-10-2004 12:31 » 

x77, а энто не поможет? :

Цитата

кстати, у Lock() такой прототип:
virtual BOOL Lock(DWORD dwTimeout = INFINITE);


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

А на крайняк - вспоминаем флаги - я не знаю, как там в системе устроены мутексы, но по сути - это всё эти несчастные флаги...
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #20 : 04-10-2004 12:36 » 

Цитата

реагировать соответственно...


например запускать вспомогательный экзешник, который убьёт этот и перезапустит заново. Если так можно Улыбаюсь
Записан

Серж
Гость
« Ответ #21 : 04-10-2004 12:59 » 

x77, я попробовал под Win2K, от мьютексов после принудительного закрытия не остается никаких следов, даже от именованных, т.е. система сама их освобожлает.
А откуда такая информация о мьютексах?
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #22 : 04-10-2004 13:38 » 

Серж, а ты полностью весь механизм синхронизации прогнал, или просто создавал несколько мьютексов в одном процесе? при синхронизации через мьютексы тебе придётся делать DuplicateHandle, чтобы получить доступ к мьютексу из другого процесса. мьютекс автоматически уничтожается, когда уничтожены все его дескрипторы. вот это меня и насторожило. если при аварийном завершении останется "мусор" в виде чьих-то хендлов, то по идее, освобождения мьютекса не произойдёт.  может я не прав, конечно, винапи не самая моя сильная сторона, но как-то связываться всё-равно не хочется Улыбаюсь
Записан

Серж
Гость
« Ответ #23 : 04-10-2004 15:20 » 

x77, не надо бояться мьютексов. Единственное, что возникает при работе, если поток, который владеет мьютексом, прерывается без освобождения, так это то, что в фунции WaitFor... в другом потоке возникает
код возврата WAIT_ABANDONED, который как раз и сигнализирует об этой ситуации, по этому коду можно как раз диагностировать ситуацию, но ни о каком зависании речи и быть не может.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #24 : 04-10-2004 16:46 » 

я провёл такой эксперимент:
Код:
WORD var;

UINT t1(LPVOID p)
{
CMutex m1(0,"m1");

for(;;)
{
if(m1.Lock(10*1000)==FALSE)
{
::AfxMessageBox("облом");
return 0;
}
(*((WORD*)p))++;
m1.Unlock();
}
}
Код:
UINT t2(LPVOID p)
{
CMutex m1(0,"m1");
for(;;)
{
m1.Lock();
for(;;);
m1.Unlock();
}
}
Код:
void CGuard2View::OnButton1() 
{
::AfxBeginThread(t1,&var);
::AfxBeginThread(t2,&var);
}

то есть один поток (t2) включает мутекс и вешает систему, а другой, прождав 10 секунд, говорит об обломе. Причём при этом запущен монитор цп. Цп - на 100% занят. А когда закрываю прогу - всё смолкает, 0% загрузки
« Последнее редактирование: 01-12-2007 16:42 от Алексей1153++ » Записан

x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #25 : 04-10-2004 17:17 » 

Серж, Алексей1153, убедили Улыбаюсь, спасибо.
Записан

Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines