Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Общие вопросы > Потоки WinApi и GCC |
Автор: NYX 13.7.2012, 21:17 | ||
Всем привет! Код
Работает! Но, есть несколько вопросов, ответы на которые в принципе можно найти, капнув в дебаггере, предварительно вкурив в низкий уровень (в котором я очень слаб)... в общем, кому не лень уделить время, буду непомерно отблагодарен ![]() * Можно ли утверждать, что отмена Sleep в деструкторе класса Threaded не повлечет за собой трудностей? Конкретизирую - после завершения потока, ОС уничтожает все-про-все и может ли в этот "переходный" момент как то пострадать основной поток при условии что он уже завершается или завершился? * Как я понимаю, статическая функция-член класса является ОБРАЗЦОМ потока, который создает ОС при вызове CreateThread? То есть можно утверждать, что N классов = N потоков? * как себя поведет класс если создать его экземпляр через new? Много там кошмаров будет? Небольшое пояснение - зачем мне это? На данный момент есть такая задача: "написать файлер на подобии стека заданий". То есть, некий класс, который предварительно принимает задания на действия и после вызова Start задания в стеке класса выполняются. Например вот так: 1) QueryRead(&UnsignedInt, sizeof(unsigned int)); 2) QueryRead(&AnsiString, &UnsignedInt); 3) QueryStart(&Percent); где прототип QueryRead позволяет использовать в качестве первого аргумента указатель на данное В которое надо прочитать данное из файла, в качестве второго аргумента либо размер читаемого данного, либо указатель на значение размера (который может указывать на ранее прочитанное данное). А так же и QueryRead(???) который может по результату чтения, как то преобразовывать данные и тутже записывать их куда то. И еще-еще QueryPosition(unsigned long long *Pos, bool Direction)... в общем вот такая увлекательная механика ![]() ![]() * Стек заданий - размещает через new структурку в которой описывается задание. Стек является порядком заданий. * Поток, который - "один объект, один поток", являющийся членом-функцией класса. В итоге должен получится вот такой вот объект, которых можно было бы сделать например штук пять в одной функции и пока выполняются потоки, функция оценивала бы процент выполнения заданий для всех ИЗВЕСТНЫХ пяти потоков + возможно выполняла бы какие то дополнительные расчеты, например обращалась бы к БД блин или чонить типо того. (?) В будущем, если все это месиво из кода окажется минимально правильным, я смогу сделать что то вроди ПАЗЛА между объектами класса и что то на подобии пула этих объектов потока, что бы как то более-менее контролировать эту массу зомбо-потоков. Реализация на данный момент не интересует, пока что важны исключительно те моменты которые в списке выше ![]() Заранево благодарствую всем уделившим время! ![]() |
Автор: Randajad 13.7.2012, 23:10 |
asio::io_service это называется из буста. Занятная штука, плюс, оно само может использовать столько потоков, сколько вы ему дадите. Довольно удобно. Когда-то давно сталкивался с ней для своих нужд, но уже все потерялось и примеров не приведу. ![]() |
Автор: borisbn 13.7.2012, 23:30 |
ага.... я, конечно, его имел в виду, а не весь asio |
Автор: NYX 13.7.2012, 23:45 |
Эт да, велосипед. Но велосипед изобрести хочется ![]() |
Автор: boostcoder 14.7.2012, 00:19 |
разумеется =) "ох и не легкая это работа, из болота тащить бегемота" (с) |
Автор: NYX 14.7.2012, 00:51 | ||
C WaitForSingleObject должно быть так?
или тот же ВаитФор в if с ожиданием напр. 1 секунды и если что либо кроме нуля, то уже валить поток принудительно перед завершением главного процесса? Опять таки, не планируется прежде временное завершение... оно нужно скорее на случай подвишивания главного потока во время выхода из программы. иного случая просто не планируется. |
Автор: boostcoder 14.7.2012, 01:34 |
зачем гадаешь? в описании же все сказано. |
Автор: Dem_max 14.7.2012, 07:21 |
я бы добавил следующие функции bool StartThread(сюда передаем указатель на функцию которую нужно выполнять в потоке); запускает поток. ExitThread(void); // послать сигнал пользовательской функции на завершение потока. bool Terminate(void); // завершить поток принудительно. bool WaitForExitThread(int WaitTime); // функция ожидания завершения потока, вызывается после ExitThread(); int GetExitCode(void); // Возвращает код завершения функции потока. bool IsStarted(void); // проверяет запущен ли поток |
Автор: NYX 15.7.2012, 01:30 |
А значение для GetExitcodeThread где хранится? Есть какой либо риск потерять возвращаемое данное? Напр. в случае разрушения потока каким либо нежелательным образом? Ну, что нибудь рядовое... бывает? Пока что проблем вроди бы не наблюдается у меня, вызываю взятие результа сразу после того как ВэитФор укажет что поток иссяк ![]() |
Автор: Dem_max 15.7.2012, 04:58 |
Кстати забыл сказать, крайне не рекомендую создавать поток в конструкторе класса, а то ты можешь в одном классе объявить несколько объектов Threaded, то при создании класса у тебя создадутся несколько нитей(потоков) и будут сразу выполнять действия, хотя эти действия должны допустим выполняться по нажатию кнопки пользователем. |
Автор: NYX 15.7.2012, 14:43 |
А если создавать в конструкторе в замороженном состоянии и размораживать поток по вызову Start например? В таком случае, выполнение потока будет более резвым, нежели "пока создастся, пока туда-сюда...". А как бы подготовить поток к запуску... и сам запуск делать только по принуждению? Я принципе на данный момент я так и реализовал. Кстати, столкнулся с некой траблой... не буду погружать в глубины, возник только один вопрос. Деструктор, он же может в своем теле использовать данные-методы-функции своего класса? Просто у меня такая фишка... в деструкторе оттягивается уничтожение объекта само собой до момента пока поток рабочий (жив или заморожен).... но в конструкторе поток заморожен, и если не вызвать Start, прога само собой виснет в ожидании завершения потока. Я в деструкторе повесил Start если поток не запущен (проверяется флаг, состояние которого выставляется в самом потоке) и если флаг false, то он ставится в true вызывается Start и сам поток в момент УСТАНОВКИ флага так же проверяет его состояние и если он ранее установлен в true то поток сразу же завершается. Если false, то ставит true и продолжает выполнение с подвызовом. Это предотвращает выполнение подвызова потока - та самая юзерская функция которая вызывается объектом-потока. В общем механизм вроди бы рабочий, однако Start не срабатывает O_O я вот думаю, может в деструкторе дело? ОллиДбг показывает что поток в момент деструктора еще заморожен и есть.. .не убит. Но пощему то чот не срабатывает. |
Автор: Dem_max 15.7.2012, 16:20 | ||||||||
собственно так и сделано у Borland
все может использовано что объявлено в классе
Для деструктора: Проверяй валиден ли хэндл потока, если да посылай сигнал завершения потока, дожидайся заврешение потока по конкретному таймауту, если таймаут истек делай Terminate потока. |
Автор: NYX 15.7.2012, 18:06 |
Де жути пугают использования TerminateThread, боюсь как бы потом не было таких ошибок... которые непойми откудасыпятся. Начитался ужасов про все виды завершения кроме естественного выхода из потока по завершению. А еще вопрос, вот полчуается как.... если распаралелить и пустить все потоки в разнобой, они ведь будут быстрее выполняться чем если их сонхронизировать? И прирост производительности, навскидку будет невысоким если синхронизировать ВСЕ потоки включая главный? |
Автор: Randajad 15.7.2012, 18:26 |
Зависит от частоты синхронизаций. Если они у вас все делать синхронно будут - прироста нет. Не вижу проблемы для создания потоков. На их создание уходит не такое уж и большое время. Если использовать std:: или boost::thread, то никаких termitate не нужно. |
Автор: Dem_max 15.7.2012, 18:32 | ||
Да ну ???? и что же случается с зависшим потоком ???? он так и остается висеть в памяти ??? |
Автор: Randajad 15.7.2012, 18:36 |
Почему он зависнет? Есть thread::join, которое и нужно юзать. |
Автор: Dem_max 15.7.2012, 18:38 |
Ну потому что например в потоке бесконечный цикл запущен. |
Автор: Randajad 15.7.2012, 18:44 |
Ну запущен - пусть делает себе бесконечный цикл, раз надо. Что такого? |
Автор: Dem_max 15.7.2012, 18:46 |
Ну поток должен быть завершен, если это требуется. Вопрос как его завершить ??? |
Автор: borisbn 15.7.2012, 20:12 | ||
вот был (и остаюсь) точно такого же мнения.... но, когда увидел boost::/std:: thread - был немного удивлён. Там как раз наоборот - запускают поток в конструкторе (и мало того - в деструкторе - не ожидают завершения работы потока)... странно... |
Автор: NYX 15.7.2012, 20:18 |
В будущем конечно я уже буду использовать готовые реализации объектов-потоков. Но пока что надо разобраться в тонкостях этих потоков. В любооом случае, это надо знать ![]() ... Все, разобрался с деструктором. Все арбайтен! Там с помощью флагов теперь поток размораживется и если подвызов потока не произошел, то поток завершается без подвызова. Если поток по каким либо причинам завис, то деструктор не даст удалить объет до того момента пока поток не развиснет. Так что поток вечен в этом плане. Если надо предотвратить работу потока (возможно принудительно) то через Stop в аргументе которого указывает интервал ожидания ![]() |
Автор: NYX 15.7.2012, 20:56 | ||||||
Прототип
Реализация Функция _Format это аналог sprintf тока возвращающий string, вместо аргумента указателя результирующей строки str Функция IternalShellError это функция которая выводит ошибку либо в консоль, либо в MessageBox и завершает приложение ExitProccess Оповещение об ошибке на скорую руку ![]()
Добавлено @ 20:58 Там в Pause используется Sleep. Дело неблагодарное, так как заснет все, не только замораживаемый поток ![]() main
|
Автор: Randajad 15.7.2012, 23:34 |
atomic_bool и в бесконечном цикле в потоке ее проверять. Разве не так? Или вы хотите с помощью Termonate его жестоко убить? ![]() |
Автор: NYX 16.7.2012, 00:03 |
атомарный флаг.... эммм... а куда его пихать то? В перерывах выполнения пользовательской функции-потока? Не, я понял.... но почему атомарный? Кто еще будет читать\писать флаг? ![]() |
Автор: Alca 16.7.2012, 00:14 | ||
https://bitbucket.org/skynowa/xlib/src/709147d9f933/Include/xLib/Sync/CxThread.h https://bitbucket.org/skynowa/xlib/src/709147d9f933/Source/Sync/CxThread.cpp |
Автор: Randajad 16.7.2012, 00:40 |
Это был на вопрос участника выше: что делать в бесконечном цикле. ![]() Проверять эту атомарную переменную и завершать выполнение цикла, если она, например, == true. Зачем атомарную? Компилятор может ее выкинуть из цикла, ибо посчитает, что она не изменяется. Хотя. Можно сделать не атомарную, а volatile. |
Автор: NYX 16.7.2012, 00:40 |
На GCC нет _beginthread :( Но для общего принципа исходники интересны. Гранд рахмад за писчу для ума! Да и ваще всем спасибо за помосч ![]() КСТАТИ! Об указании на НЕОБХОДИМОСТЬ ПРИСУТСТВИЯ! Надо и это мне тоже учесть... таааак. http://alenacpp.blogspot.com/2006/04/volatile.html |
Автор: Dem_max 16.7.2012, 05:00 | ||
Бесконечный цикл это к примеру. Ну в коде с большой вероятностью может присутствовать такая строчка WaitForSingleObject(hHandle, INFINITE); Ну и если событие по каким то причинам никогда не наступит, то все поток весит. Никакие флаги не спасут отца русской демократии. |
Автор: Randajad 16.7.2012, 10:33 |
Говнокодить не надо, чтобы оно по каким-то причинам не наступило. ![]() Добавлено через 25 секунд Это к сабжу уже не относится, кстати. ![]() |
Автор: Dem_max 16.7.2012, 12:09 | ||
Ну в сабже есть такой как раз момент. Если эта функция будет в потоке, который породил еще один поток.
|
Автор: NYX 16.7.2012, 13:11 |
В реальности врядли будет вообще использоваться эта функция ![]() ![]() ![]() |
Автор: NYX 16.7.2012, 18:10 |
Расковырял инфы про критические секции. Собстно вот статья http://www.rsdn.ru/article/baseserv/critsec.xml Стало быть: * Критическая секция это объект, размещение которого определяется функциями EnterCriticalSection и LeaveCriticalSection в параметрах которых принимается указатель на объект секции. Далее, перед оперированием какого либо данного, делается ентер, по завершению оперирования лив. Соответственно можно использовать вложенные ентеры и ливы, если например поток может обращаться ЛИБО - ЛИБО соответственно. так же выходит, область критической секции не начнет свое выполнение и не прервется по середине. Весч прикольная. Но почему тогда такие лакомые плюшки мало где используются? Напр. сервера, где почти во всех как в одном юзаются мьютексы или симафоры. В сильно взрослых серверах типо апача (мельком просмотрел) тоже вроди бы мьютексы, в микросервере null httpd... А что стоило бы, например, внутренние данные объекта потока защитить критсекциями... плюс, дать возможность объекту-потоку принимать объект критсекции, или более того... если имеется подвызов, то просто обуславливать его вызовами ентер и лив. Тогда ВСЕ используемое в потоках- потомках коллектора будет (типо)атомарным. А в случае если например используется связка коллекторов... то можно использовать нечто вроди массива (стека?) объектов критических секций. Довести до автоматизма опрос списка секций в выполнении определенных задач (ну типо не все элементы объекта, а зависимые от выполнимой задачи). Ух! Сколько всего придется погрызть и попробовать на зуб. |
Автор: Dem_max 16.7.2012, 18:24 | ||||||
Типо атомарным не будет никак, атомарнось подразумевает выполнение чего либо за один такт процессора.
Используются
|
Автор: NYX 16.7.2012, 19:19 |
Ну да но я и написал ТИПО ![]() ![]() ![]() ![]() ![]() |
Автор: bsa 17.7.2012, 12:54 | ||
![]() Может все-таки стоит почитать http://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F? |
Автор: NYX 17.7.2012, 15:29 |
Типо конвеерность все такое ![]() |
Автор: NYX 19.7.2012, 14:07 |
Все! Я Запутался. |
Автор: bsa 19.7.2012, 14:19 |
в чем? в атомарности? |
Автор: NYX 19.7.2012, 16:53 |
Да в ней акаянной! Вот, критические секциии ваще вынесли мне мозг. Ступор произошел еще в самом начале, когда была попытка как то изолировать данные класса-потока от множественных обращений к ним из: 1) внутренней реализации потока 2) внешних вызовов других потоков в т.ч. главного потока процесса Путаница началась с вложенностью критических секций, а так же возможность дедлока в случае витиеватых возможностей класса. В какой то момент он может получиться, в какой то - нет. ООООЙ БОЖЕ!!! Это какой то АД! Вот логически потоки понять реально. А вот как сделать наиболее примитивную - простую автомарность. Пусть даже код будет избыточным, но его понимание было бы простым. Я уже подумываю сделать реализацию потоков и атомарности с помощью низкого уровня. Мне кажется что такая реализация даже будет лушче. Например, привязка данного к потоку. В случае если от коллектора ответвлено более одного потока, коллектор не опзволит обратиться к подключенным к нему данным. Это всего то лишь надо сделать подобие встроенных типов данных, а точнее дополнить POD парочкой флагов и полей под идентификатор коллектора. В общем решить на низком уровне будет от части и сложнее и проще... но блин опять же гемор, это все будет делаться ВНЕ реализации какого либо проекта и если надо будет подызменить реализацию, надо будет пересобирать например ту же DLL работающую с потоками и переподключать.... в общем пипец. Может есть какая то стандартная схема гарантирующая атомарность данных? что бы можно было бы буквально просто указывать, -> вот тебе внешние данные товарищь поток, читай там оверхед (или какой нибудь объект рядом с ними) и смотри, не балуй с чтением\записью. Да, кстати, в критических секциях я запутался в силу того, что не понял как они работают в низах. Вот допустим, выделил я внутри реализации потока блок кода и работы с данными. Окей. Второй, дублирующий этот код в своем потоке - поток, не может обратиться до тех пор пока ЭТА СЕКЦИЯ кем -то занята. Окей. с этим все ясно. А если допустим, я хочу изолировать вызовы printf для двух потоков в двух областях кода? Это надо писать и там и там EnterCriticalSection. Окей, а если рядом с вызовом printf используется инициализация какого то обхекта, и делается в одном из двух экземпляре. Но опять же может использоваться другими двумя потоками. Надо делать опять очередную критсекцию? Обобщаться все эти блоки, для выявления общих секций, которые не нарушили бы данные... блин это просто АД! ![]() |
Автор: NYX 19.7.2012, 19:52 |
![]() мои мозги в кофемолке ![]() |
Автор: bsa 19.7.2012, 23:46 | ||||
думаю, это вообще мало кто представляет. ![]()
просто Microsoft назвала критической секцией то, что обычно называют захватом мьютекса. Так как они мьютекс сделали ядерным. Так вот, забудь про ядерный мьютекс. Речь идет о межпоточных мьютексах (фьютексах). Реализуются они через простейший атомарные операции процессора. При захвате мьютекса происходит изменение целочисленной переменной. При одном значении мьютекс считается успешно захваченным, при других значениях он считается захваченным другим процессом, в итоге текущий поток передает управление ядру. При возврате управления происходит еще одна попытка захвата... Если интересуют детали, то см. http://locklessinc.com/articles/mutex_cv_futex/ |
Автор: NYX 20.7.2012, 00:48 |
Стало быть сложность критических секций заключается в том, что нет ЯВНОСТИ указания "кто ваще кого чо?". Фьютекс является "Сходным образом оптимизированы объекты CRITICAL_SECTION в Win32 API, а также FAST_MUTEX в ядре Windows" по словам википедии. Сейчас более менее начал вникать в назначение мутексов и семафоров, благодаря какому то документу из НГУ на тему "Аспекты параллелизма", который описывает аспекты и реализации для Win\Linux после основополагающей теории. Буквально спас меня этот документ. Он был послан богами свыше, однозначно ![]() Я решил поставить для себя такую задачу, которая не относится к реальным требованиям или реальным мирским потребностям. Выучить потоки на примере сервера, где модель сервера примерно такая: 1) поток который обрабатывает входящие данные и формироует очередь сообщений (подразумевается логика писатель-читатель) 2) множество потоков которым отведена ЧАСТЬ очереди (группировка и разнообразие потоков может диктоваться например скоростью соединения) 3) поток который производит рассылку обработанных данных по клиентам. Как можно реализовать такую модель? Какие средства обеспечения атомарности данных необходимы для реализации. Попробую налету сформировать подзадачи. 1) Поток принимающий данные от пользователя, позволяет засыпать\просыпаться потоку 2го пункта. Это важно! На случай если клиентов очень много но все они в разных категориях. Так, получится что для медленных клиентов с нечастыми запросами поток будет засыпать чуть чаще, но приоритет таких потоков (на уровне выполнения ОС) будет немного выше других. 2) потоки обрабатывающие данные, должны учитывать определенные условия. Например, поступившие данные от U1 на загрузку файла. Но, в процессе загрузки файла, U2 изменил пару байт. Варианта два - 1) прекратить закачку, дождаться обновления и самовозобновить закачку ( эгоизм среди потоков! О да, рекомендуемо). 2) Дождаться закачки - обновить данные (не рекомендуемо в силу больших потреблений ресурсов ЦП и трафика, так как более новое данное один шишъ придется закачивать по новой, но юзверь может и не знать о том, что данное было обновлено!!!!! ЭТО ВАЖНО!). 3) Потоки рассылки, являются чем то вроди дублирующего усилителя (аля РЭА). Где например, находящиеся в ОДНОМ ЗАЛЕ ОБМЕНА ФАЙЛАМИ учитываются такие условия описанные в пункте 2. То есть, грубо говоря, сервер и потоки могут быть уверены, что U1 не нарушит данные обрабатываемые в другом ЗАЛЕ для U234-517. Сами по себе потоки, могут пробуждаться в случае если очередь не пустая. Опять же, потоки могут обрабатывать отдельные части очередей. ХОТЯ! Все эти области очередей можно представить в виде множества стеков ![]() ДАДАДА! Что я еще заметил! У пользователя можно установить мин-макс ( 0 < ?) пунктов очереди, и уже ранее сконфигурированный сервер при авторизации пользователя, создал бы болванку пустой очереди! Хотя в представлении очереди как СТЕКА это очень сложно... представить... маллок и реаллок и прочее что ли? В общем это второстепенно уже наверно. Это образно. Но я думаю что выполнив такую задачу, потоки будут для меня семечками. Хочется додуматься самому, но сейчас понимаю, что на это надо время, поэтому взываю о помощи опытных людей ![]() Как вы думаете? Вот навскидку, без кода и примеров, сугубо на пальцах, как такое реализовать используя мьютексы-семафоры-критсекции? Что будет избыточным? Что будет необходимым? ![]() стало быть имеется получается вот что: U1 -\_ QS1 ~ TI -> TC -> TO (thread in -> thread calc -> thread out) U2 -/ U3 - QS2 ~ TI -> TC -> TO U4 -\ U5 - |_ QS3 ~ TI -> TC -> TO U6 -/ То есть в своем начале, каждый юзер имеет 3 потока Вход -> обработка -> выход, и в случае (ухахаха ИНТИМНОЙ) близости юзверей, их потоки сливаются в едином экстазе обрабатывая ВСЕ их запросы по некой событийности. Значит, общими данными могут явиться данные, у которых более 1го владельца. Вроди бы логично получается. Чего стоит указать потоку с каким мьютексом из множества он имеет дело.. допустим так. Далее, отделиться от очереди и перейти в свою... может означать копирование не обработанных данных СТАРОЙ ОЧЕРЕДИ, ровно таким же образом как и слияние очередей... но можно сделать очередь для множества, как массив очередей, где i оперируемая поток очередь на данный момент времени. Очередь является атомарным данным (ну или для корректности, множеством атомарных данных. Хотя первое наверно точнее, так как элементы в целом и есть неделимое для потоков данное). Окей, с этим более менее разобрались. Но как внутри этой всей схемы, идентифицировать юзверей например по категории скорости соединения? Так как -> * Пользователи со скоростью соединения ОТ и ДО принадлежат одной группе, а те что от ДО и ДО-ДО ко второй.... * Те кто в группе с низким пропускным каналом обрабатываются приоритетнее чем те у кого канал быстрее. Если в очереди стоят 3-4 потока на группу быстрых клиентов и появляется медленный, то после НЫНЕШНЕГО клиента сразу идет медленный, после чего обслуживаются те 3-4 быстрых, если более нет медленных. ДА! Только так. Сервер можно будет допустим конфигурировать низший порог скорости + кол-во низших скоростных клиентов, дабы не приводить к обработке ТОЛЬКО низших клиентов. Это уже иная задача и в целом это дело можно контролировать например вручную, админам! Иначе, для чего они тогда нужны админы, как если не для этого ![]() ДАЛЕЕ! Уже вроди бы более менее мне ясно что да как.... но вот вопрос ![]() |
Автор: Dem_max 20.7.2012, 04:00 |
кстати почитай http://wm-help.net/books-online/book/59464/59464-27.html#h8 http://wm-help.net/books-online/book/59464/59464-28.html#h9 http://wm-help.net/books-online/book/59464/59464-3.html#h10 http://wm-help.net/books-online/book/59464/59464-4.html#h11 |
Автор: NYX 20.7.2012, 05:21 |
Спасибо! Читаю. Вот только что нарвался на эту серию статей еще http://www.sofmos.com/lyosha/Articles/multithreading1.html Затарился пищей для ума. Читаю ![]() |
Автор: bsa 20.7.2012, 11:07 | ||||
NYX, код под Windows:
Семафоры имеет смысл использовать когда у тебя типичная схема из m производителей и n потребителей. Производители что-то делают, добавляют в очередь и сигнализируют семафором. Потребители ждут сигнала, берут из очереди и обрабатывают. Мьютексы нужны тогда, когда несколько потоков что-то делают с одними и теми же данными, при этом как минимум один из них их меняет. В этом случае, каждый поток перед каждым обращением к общим данным должен захватить мьютекс, выполнить операцию и освободить его. Естественно, что для каждого блока данных должен быть свой мьютекс (общий для всех потоков, конечно). |
Автор: NYX 20.7.2012, 19:03 |
Ясно. А как быть с приоритетами? Допустим если поток записи более приоритетный, то есть его надо протиснуть в очередь сразу после ВЫПОЛНЯЮЩЕОСЯ не смотря на имеющуюся очередь из читателей? Вот везде описывается что такая возможность допустима... но никак не пойму, как это сделать. Это решается на уровне приоритетов ОС? То есть как вот приоритет для процесса устанавливается.... |
Автор: bsa 20.7.2012, 20:56 |
Процессы - это исполняющиеся экземпляры программ. А потоки - это составные части одного процесса. Не путай понятия. Время блокировки нужно делать минимальным. Т.е. на время выполнения простейших операций добавления/извлечения данных из очереди. Поэтому в большинстве случае проблем не будет. Если же у тебя все так сильно нагружено, то копай в сторону lockless. |
Автор: NYX 20.7.2012, 21:42 |
Да, на счет процессов и потоков я знаю, процесс это своего рода набор потоков, где есть первичный поток и возможность создания дополнительных. По сути процесс это набор данных (оверхед). На счет нагрузки даже примерно сказать не могу ![]() ![]() ![]() ![]() 1) Удаление старого пункта задачи для пользователя и присабачивание нового 2) Если напор уж очень сильный, то просто напросто игнорировать. Кроме того FILO не будет замедлять работу сервера в случае если в очереди имеется от 2х заданий. Так как непосредственный доступ к отдельным данным он наверно всеж допустим. Если использовать ВСЮ очередь как целое данное, то думаю ваще смысла нет FILO оно или FIFO. А на счет приоритетов потоков... Sleep(0) пока не найдется нужный поток? ![]() |
Автор: bsa 22.7.2012, 23:03 |
Есть у меня подозрение, что ты путаешь FIFO и FILO. FILO - это стек (first in last out - первым пришел, последним уйдешь). Т.е. ты читаешь самые свежие данные, а самые старые могут лежать до скончания века. Стек нет смысла использовать в качестве очереди. |
Автор: NYX 23.7.2012, 22:03 |
вот в том то и дело, что что это даже не стек, а фиксированная очередь, обслуживаемая несколькими потоками, для каждого отведена определенная часть очереди. Очередь фиксированная, ее размер позволяет определить степень загаженности, и если кол-во запросов от пользователя превышает очередь, следующие запросы игнорируются. На например, пользователь сломал мышку, она лаганула и 50 раз нажала кнопку ОТПРАВИТЬ ![]() ![]() А кто нибудь может что-то сказать про TLS (тот что Thread Local Storage)? Что это такое ваще? ![]() |
Автор: xvr 24.7.2012, 10:27 |
Могу сказать - забудьте про них пока ![]() У вас задача (про потоки и обслуживание пользователей) от поста к посту видоизменяется быстрее, чем размножаются кролики ![]() ![]() |
Автор: bsa 24.7.2012, 13:35 |
Это и есть FIFO (first in first out - раньше придешь, раньше уйдешь) - стандартный контейнер std::queue. Очередь (FIFO) она и есть очередь. А стек (LIFO или FILO) - это не очередь (контейнер std::stack), это хранилище данных с обратной очередностью извлечения. |
Автор: NYX 25.7.2012, 01:35 |
вот что я имел ввиду -> (новый) in -> ||||||||| -> out (старый) Как бы поступает с начала и обслуживается с конца (или наоборот. Смысл один получаетсо ![]() ![]() ![]() |
Автор: volatile 25.7.2012, 01:55 |
Это есть, первым пришел, первым уйдешь. FIFO |
Автор: NYX 25.7.2012, 02:02 |
Блин, ну вот последовательность алгоритмическая: 1) Добавили новый элемент A 2) (возможно параллельно) Извлекли старый элемент Д ![]() ![]() вот допустим, есть у нас цепочка тэ динамических элементов, где 0 элемент имеет указатель на следующий и предыдущий. Новым будет элемент тот, на который указывает element[0].pprev а старым будет element[9].pnext и вот пока один поток цепляет [0].pprev второй поток отцепляет [9].pnext (индексы элементов я поставил для условности чоб яснее было) ![]() ![]() ![]() ![]() ![]() |
Автор: volatile 25.7.2012, 02:29 |
Да, это FIFO |
Автор: NYX 25.7.2012, 02:40 |
Черт побери, я был уверен что это нечто иное. Ну да ладно, главно суть теперь ясна. А что вы можете сказать о такой методе очереди для серверного ПО, в тех условиях где нет выделеного потока для каждого клиента, а клиенты делятся по группам, которые обслуживают потоки и на один поток приходится массив клиентов? Да и ваще, может я как неопытный, не вижу каких то подвохов в таком подходе? Получается как... * клиент приконнектилсо, ему выделилась очередь обладающая счетчиком контролирующим кол-во элементов очереди * если клиент попадает в некую группу, его очередь располагается в потоке ГРУППЫ * если клиент единственный для своей группы, или является грубо говоря нераспределенным пользователем, его очередь пихается в поток для непонятных клиентов (ну грубо говоря так). Если находится еще один клиент такой же группы, его к нему туда.. к первому АБСТРАКТНОМУ клиенту в поток, и поток уже обслуживает массив из двух очередей. в цикле из двух итераций пробегается по очередям, обрабатывает сначала 1го клиента, размещает в очередь на отдачу результата, потом второго клиента... и так далее. Это не веб сервер это ваще хз чо за сервер... ну будем считать что это пусть будет допустим чат с залами для конференции. Блин наверно такое даже больше подходит. * В контексте одного потока, данные очереди не могут обрабатываться другими, сторонними потоками. Это будет уже нелогично, если человек из конференции А напишит в конференцию Б ![]() ![]() вот собственно я уже и представляю как все это примерно выглядит. Я могу немного не адекватно выражаться, можете меня не понять... просто я не очень силен в терминах и ухахаха не всегда правильно трактую то что имею ввиду, так как всегда тороплюсь ![]() А пугает меня такая вещь как масштабирование такого сервера ![]() есть разве что идея режимности сервера. Как например: * режим 1 - сервер выполняет все три потока для группы * режим 2 - сервер выполняет только прием \ отдачу * режим 3 - сервер выполняет только калькуляцию (сердце формирования очереди отдачи + допустим фильтрация мата в чате) И если надо наростить мощность... сервер 1го режима принимает в качестве определенной команды (может даже по удаленному терминалу) нечто вроди "rembind 192.168.1.2" и это делает следующее: * очередь опустошается * формируется новая очередь уже на новом калькуляторном сервере * элегантно завершаются потоки калькуляции на первом сервере * далее все дело функционирует на двух серверах, где время калькуляции обуславливается скоростью второго физического сервера который в свою очередь так же может давать запросы на сервер с БД Какое ваще мнение на такую модельку? Даже пусть сложно, фиг с ним я не тороплюсь никуда, это просто эксперемент для себя ![]() |
Автор: volatile 25.7.2012, 10:30 |
NYX, ну очень сумбурно... ![]() По возможности переполненя имхо, вообще по-барабану какой тип очереди. (FIFO или FILO) Все зависит от соотношения помещающих и извлекающих потоков. Защиту от переполнения нужно вводить в любом случае. По быстродействию, тоже вобщем-то по барабану. Накладные расходы по организации очереди, (в вашем случае) будут ничтожны по сравнению с обработкой данных. Так что тип очереди и здесь не имеет особого значения. По здравому смыслу, конечно, нужно использовать FIFO. т.е. обслуживание в порядке поступления, как в очереди за сосисками. Вообще, общий совет: думайте больше о логичности, а не о быстродействии. Ясно устроенный механизм, легче будет и оптимизировать. Но это уже потом, если будет на то необходимость. |
Автор: NYX 25.7.2012, 14:15 |
Ну я тоже думаю что слишком уж заморочился. Просто как то диковато воспринимать факт динамической ячейки для очереди ![]() точнее переодического распределения памяти. А если допустим создать 10 ячеек, и оперировать как то может быть индексом (указателем элемента), может быть было бы быстрее ![]() ![]() ![]() А на счет переполнения, я не ставил акцент на контроль переполнения ![]() ![]() ![]() |
Автор: Alca 25.7.2012, 15:27 | ||
http://forum.vingrad.ru/forum/topic-60076/view-all.html |
Автор: NYX 25.7.2012, 15:31 |
Спасибо. Читаю. Начало угнетающее ![]() |
Автор: bsa 25.7.2012, 16:55 | ||
Вот только откуда старый элемент Д взялся? Надо рассматривать с самого начала: 1. очередь пуста 2. в очередь добавлен элемент А 3. в очередь добавлен элемент Б 4. из очереди извлечен элемент А и добавлен В 5. из очереди извлечен элемент Б ... Элемент А встал в очередь первым. И первым же ее покинул. |
Автор: NYX 25.7.2012, 17:40 |
Да именно так и есть ![]() ![]() |
Автор: NYX 29.7.2012, 21:44 | ||
Ребят, допустимо ли такое использование критической секции?
Не будет ли трудностей если два потока одновременно вызовут одну функцию одного объекта, при условии что внутри функции автоматически используется критическая секция? |
Автор: bsa 29.7.2012, 22:12 | ||
NYX, собственно именно для этого критические секции и существуют. Рекомендую тебе использовать RAII для организации критических секций:
|
Автор: SVN74 29.7.2012, 22:16 |
Простите, если я не правильно понял схему желаемого сервера, но я бы сделал так: Создал класс в котором прикрепил 1 - "map" - для базы клиентов, 2 - сокетный протокол (сервер, клиент), 3 - сохранения баз данных. Затем это все размножил бы класс по количеству групп, - дав каждой группе свой порт ожидания. В итоге получиться независимые группы со своими серверами и базами данных без необходимости синхронизаций... Добавлю: Тоже самое можно создать и на одном ожидающем порту, только потом разбрасывать каждому классу отдельно его клиента, но тут уже надо будет одну синхронизужку делать.. |
Автор: NYX 29.7.2012, 22:36 |
Можно было бы вообще группы клиентов разбить на процессы отдельные. Но, допустим челу надо скаконуть из одноу группы, чонить написать и смотаться в другую.... группы динамические и основной акцент на скорость их формирования. Можно было бы выделять например 2-4 пустые группы, в случае чего юзвери туда могли бы прошмыгнуть создав новую. Переход в уже готовую группу (где есть участники, не темперную) производился бы просто под средством присваивания пары указателей ![]() А) нераспределенные пользователи (общий чат) Б) группа (конференция) Группа А всего одна и из нее юзвери переводятся в любую из множества групп Б. Сам переход обуславливается присваиванию указателей на ЧАСТЬ очереди, которую надо обработать в потоках группы. То есть переход, есть не что иное как "ткнуть поток носом в миску, откуда лакать.". Если речь идет о новой группе... она как бы и есть но ее как бы и нет. То есть, создание группы условно говоря это когда боле 1го собеседника находятся в общей комнате. Сама общая комната разумеется делается по запросу. Запрос выглядит примерно так "отцепи меня от нераспределенной группы и сделай отдельной группой вон с тем чуваком". Создается новых трипотока куда заведомо посылаются указатели на область очереди 2х собеседников. Очередь есть не что иное как глобальный массив стеков. Он есть до тех пор, пока есть пользователь. То есть основная нагрузка падает только на создание потоков обслуживания: * вход. трафик * калькуляция * выход. трафик Но блин я хз как масштабируемость делать. Как можно с такой схемой распределить нагрузку. Пока что на заметке у меня memory-maped file, но я еще не знаю можно ли использовать удаленно их и вообще выгодно ли это. Если это не выгодно, то кирдык-бабай. Если авалибл, то тогда шаровый мемори-мэпед файл, деление сервера на 2 составляющих 1) обработка i\o очередей (стеков) 2) калькуляция -> 3) база данных (ну это еще хз нужна ли она для чата, но пусть будем считать что нужна для хранения последних нцати сообщений, что бы вновь прибывшие смогли прочитать их при джоинте) каждый из пунктов является отдельным VPS (наверно на физическом уровне). Блин а еще вот я так думаю, сколько еще ждать до момент MinGW в стандарте С++11 ![]() |
Автор: SVN74 29.7.2012, 22:45 | ||
![]() И все же какова тогда выгода от группирования, если клиенту необходимо давать доступ к другой группе. Может тогда и клиента размножить на несколько групп одновременно? По моему мнению, - группы на то и группы, чтобы не контактировать... |
Автор: NYX 29.7.2012, 22:46 |
Про RAII - я мальца сократил код. Ваще где то подглядел обертку для критсекций, там в конструкторе делается инициализация, в дуструкторе соответственно... публики лок, трайлок, анлок. В реальном счас исходнике у меня эта обертка трудится. Создается некий объект, пусть это будет отображение чего-то там где то в консольке и к этому объекту имеется доступ с двухнцати потоков. И внутри потока уже нет необходимости делать эти постоянные вызовы. В пространстве объекта уже есть обертка которая используется во всех потенциально-потокововызываемых функциях. Но меня немного смутило (Опять таки я дуб в низкоуровневом, постепенно вникаю по мере возможности, но отдельно изучению отладке время не уделяю) то что функция объекта одна и вызов функции двойной, а вот тело функции уже лок-анлок. (очень коротко сказал). просто в случае критсекций в потоке, получается как, не выполняется тело потока (потоки ведь разные экземпляры функций и тел или один и тот же тел?) и именно тело лочится. А в случае функции объекта, это уже ... одна и та же получается. Я только в этом усомнился. В целом, если я предполагаю что использовать такие возможности допустимо, то наверно я вкурил в эту тему ![]() Добавлено @ 22:49 Клиента размножать?... ну ваще длительность потоков она наверно более высока нежели длительность многоликости клиента. Хотя... Я ваще не хочу создавать много потоков. Блин это отстой. Но я пока не подкован в потоках и не знаю какие сложности в производительности возникнут. Я еще краем уха слышал что потоки можно привязать к N процессору. Так ли это? Реализуемо ли это на WinAPI? Я так и не понял. Буду искать. Если размножать клиента, то тогда ваще можно использовать от силы несколько потоков, для групп и нераспределенных юзверей. Тогда все будет диктоваться объектом очереди, где уже будет указано кто есть who ![]() |
Автор: SVN74 29.7.2012, 22:55 |
Дело в том, что эффективность потоков заключается в самостоятельности выполнения задачи каждым потоком индивидуально... В Вашем случае, при большом количестве пересекающихся ожиданий (общих ресурсов) - теряется смысл и скорость работы всей программы, может тогда вообще все создать на одном потоке. |
Автор: NYX 29.7.2012, 22:57 |
На одном потоке тоже было бы круто, но... мне не принципиальны потоки. Я так опнимаю самое главное это масштабируемость сервера и грамотное распределение нагрузки и лучше ваще автоматизированное. Если это возможно на одном потоке? Хм, быть одному потоку. Просто если потоков много, и юзвери допустим молчат, поток замораживается. В этом и прелесть. А однопоточное ПО оно будет безконца парсить очереди на предмет наличия. Либо так же создавать флаги наличия позиций очереди... |
Автор: SVN74 29.7.2012, 22:59 | ||
А сколько будет предположительное количество клиентов одновременно работать? |
Автор: NYX 29.7.2012, 23:02 |
Пальцем в небо - тысяща кляентоф :] но опять же, что делать с наплывом зомбочатеров? Вдруг народу захочется поболтать, куда их посылать? Мимо или на размножаемые серверы с логин-сервера? или как васче делать то? Если бы я знал как организовать шаровые ресурсы между двумя физическими серверами при условие что шаровые данные будут под треды и разделяемыми... знать-бы вот... |
Автор: SVN74 29.7.2012, 23:05 |
![]() Та Вы что ? Забудьте о многопотоковости, это для Вас убийство, только неблокирующий сокет и общая база... Максимум 200 клиентов можно гонять по потокам, а если выше, только через один неблокирующий поток... |
Автор: bsa 29.7.2012, 23:08 | ||
причем тут RAII?
Молодец. Теперь подсмотри где-нибудь обертку, которая производит таким же образом еще и захват с освобождением мьютекса. Например, в бусте. |
Автор: NYX 29.7.2012, 23:09 | ||
Если так, стиснув зубы... VPS и размножение на многих провах оно как бы, ну платное что ли. И допустим изначально гонять несколько отдельных серверов накладно, при том что кол-во пользователей может снижаться и варьироваться. А если бы например сделать на трех потоках для групп, то тогда можно было бы ОДНО И ТО ЖЕ ПО запускать уже в трех режимах. 1) как у же помоему писал - полноценный сервер 2) сервер в режиме прием-отдача 3) сервер калькуляций (операция с БД, проверка на содержание мата и прочее прочее) В этом случае, получается цепь TI - thread in TC - thread calc TO - thread out QI - queue in QO - queue out 1) TI -> QI -> TC -> OQ -> TO 2) TI -> QI ... ... ... OQ -> TO 3) ... ... QI -> TC -> OQ ... ... тогда скорость калькуляции была бы быстрее. Для пунктов 2 и 3 очереди были бы шаровыми, может и по экземпляру очереди на сервак, а может располагались бы например QI на 2, а OQ на 3. Но это единственное до чего я смог догадаться ![]() Добавлено @ 23:12 bsa,
Брр. В смысле? не, ну мьютек принципиально... ща-ща. Неявным образом захват мьютекса это будет создание объекта и деструктор уже будет выполняться по итогу выполнения функции. Окей... моя обертка работает так же ![]() Я понял в чем смысил. Если честно это очень здоровски использовать такие пряники. Так как можно создать весьма не сложные потокобезопасные объекты. А может даже и вообще сделать так, что бы все разделяемости делались закулисами. |
Автор: NYX 29.7.2012, 23:35 | ||
А в бусте в каком направлении смотреть?
|
Автор: bsa 30.7.2012, 10:45 |
boost::thread. Там есть класс mutex (ты его называешь критической секцией, - очень жаль, что ты меня так и не понял и пытаешься использовать везде термины windows). |
Автор: NYX 30.7.2012, 17:44 |
да я уже глянул исходники, я понял смысл. передается объект в конструктор и по завершению работы функции срабатывает деструктор который этот самый мутекс и освобождает ![]() В плане не понял? Про критсекции? Я знаю что это не мутексы и ваще это некий гибрид. У критсекций минус есть, они не могут быть доступны из других процессов. Поэтому критсекции удобно использовать для каких то внутренних атомарностей объектов. Если речь идет о шаровых данных, то мутексы \ семафоры удобнее, так как с их помощью можно добиться немного более лучшего эффекта, нежели выстраивание потоков в ряд. |
Автор: NYX 30.7.2012, 18:59 |
концептуально читатели-писатели на мьютексахЪ |
Автор: NYX 30.7.2012, 19:00 |
Семафоры, мониторы и синхронные сообщения |
Автор: bsa 30.7.2012, 22:05 | ||
Ты опять меня не понимаешь. Такое ощущение, что ты не читаешь что я тебе пишу. А видишь только ряд ключевых слов, но смысл предложений до тебя не доходит. Читай внимательно: В WINDOWS КРИТИЧЕСКОЙ СЕКЦИЕЙ НАЗЫВАЕТСЯ ТО, ЧТО ВЕЗДЕ НАЗЫВАЕТСЯ МЬЮТЕКСОМ. Именно поэтому, термин "критическая секция" лучше вообще не употреблять, кроме как в контексте WinAPI (именно на уровне реализации, а на уровне обсуждения стратегий и алгоритмов следует использовать mutex). |
Автор: NYX 31.7.2012, 10:47 |
bsa, вот всегда так. Я тебя прекрасно понял и написал аж несколько раз что критсекция в моем понимании это гибрид и что изначально есть мьютексы, семафоры дейкстры и многое другое ![]() ![]() ![]() ![]() |