Вопрос № 53328: Привет эксперты. Буду ссылаться на вопрос номер 53142. Я имеел виду вообще без Win Api, реализовать многопоточность. Подскажите что почитать. К примеру знаменитая игра Ларри ,тогда ж винды ещё не было и соответствующего API. Так вот там сразу и музык...Вопрос № 53368: #include <iostream.h>
int main()
{
int x;
x = 2.3;
cout << x;
}
Заметил такую особенность, прикол в общем такой, если ввести такой
код то, у меня почему то компилятор не ругается на то что дол...Вопрос № 53370: Вот программа.
void main()
{
}
Почему она после компиляции весит аж целых 6,169 кб с копейками
Она же ничего не делает. Объясните. Если с Delphi и его VCL понятно, то в чём здесь прикол тогда.
И ещё по адресам от 0x000002...
Вопрос № 53.328
Привет эксперты. Буду ссылаться на вопрос номер 53142. Я имеел виду вообще без Win Api, реализовать многопоточность. Подскажите что почитать. К примеру знаменитая игра Ларри ,тогда ж винды ещё не было и соответствующего API. Так вот там сразу и музыка и видео одновременно. Графика т.е. Спасибо.
Отвечает: C4tnt
Здравствуйте, Александр Михайлович!
Предлагаю несколько "демонический" способ :-)
Игра делится на "секции", реализованные в отдельных функциях:
1. Графика
2. Звук
3. Опрос внешних устройств
4. Обновление игрового пространства (Refresh deamon)
5. Система скриптов
Могут присутствовать не все блоки; некоторые блоки могут быть объеденены.
Разбиваем рабочее время игры на "Фреймы".
Пусть наши секции реализованы в функциях(демонах):
Дальше распределяем функции по фреймам, например цикл из восьми фреймов:
1/8 - GraphicsDeamon
2/8 - SoundDeamon
3/8 - GraphicsDeamon
4/8 - IoDeamon
5/8 - GraphicsDeamon
6/8 - RefreshDeamon
7/8 - GraphicsDeamon
8/8 - ScriptDeamon
Каждая из функций выполняет короткое действие, например рисует кадр или
заполняет буффер звуковой карты. Для связи этих функций используются общие участки памяти. Вызываем их соответственно по очереди. У каждого демона может быть своя входная очередь,
в которую попадают запросы от других демонов (собственно их функция и будет обрабатывать).
Вы можете использовать такую систему для эмуляции потоков (Threads) в пределах одной
программы. Для этого достаточно создать список потоков и вызывать функции из списка по очереди. Такую систему иногда дополняют системой тайм-аутов, т.е. изыеряют время, за которое
выполняется функция, если она выполняется слишком долго - следующие несколько раз её не вызывают, чтобы не тормозить систему. Можно реализовать демонов так, чтобы они сообщали о
своей загруженности и распределять между ними время соответственно нагрузке. Это полезно в
случаях, когда ,например, новых звуковых команд не предвидится, а графическому демону крайне
не хватает ресурсов. Если вы имеете доступ к прерываниям - можно сделать защиту от зависания демонов, проверяя в прерывании, сколько уже времени демон работает, и при необходимости передать управление не на демон, а на основной блок.
Многозадачность на однопроцессорных машинах обычно реализуется именно так.
У процессоров (с 386) есть защищённый режим, в котором всё это реализовано аппаратно.
P.S.
1. В Windows до защищённого режима не добраться (сама Win там и сидит)
2. В ScriptDeamon тоже крайне рекомендуется многопоточность. Или можно плодить этих демонов
по необходимости.
3. Игры редко используют Windows-многопоточность.
4. RefreshDeamon и ScriptDeamon разделены намеренно. Процесс выполнения команд из скриптов
не должен мешать процессу выполнения самих скриптов.
5. Написал подробно, насколько смог
--------- Теперь к нашим ответам осталось лишь найти вопросы
Ответ отправил: C4tnt (статус: 2-ой класс)
Ответ отправлен: 26.08.2006, 13:01
Отвечает: Sergijj
Здравствуйте, Александр Михайлович!
Аналогично буду ссылаться на мой ответ на вопрос 53142.
Из моего ответа видно, что для Ваших нужд подходит вариант №2 (линейный алгоритм выполнения с направляющим хэндлером).
1. Запускается основная программа.
2. Инициализируются в статических переменных начальные состояния всех подпрограм.
3. Вызывается хэндлер (подпрограмма последовательности запуска подпрограм), который "знает" чего и когда запускать.
4. Хэндлер вызывает подпрограмму рисования картинки.
5. Подпрограмма рисования картинки проверяет своё текущее состояние (читает из статической переменной) и рисует ОДИН кадр, кот, который нужен СЕЙЧАС, на данном этапе процесса.
6. Подпрограмма рисования картинки записывает своё текущее состояние в статические переменные.
7. Программа рисования картинки отдаёт управление хэндлеру.
8. Хэндлер вызывает подпрограмму играния музыки.
9. Подпрограмма проигрывания музыки проверяет своё текущее состояние из статических переменных и если настала пора менять ноту, то посылает на микросхему спикера соответствующую команду (на изменение высоты звучания спикера). Т.е. играем ОДНУ ноту.
10. Подпрограмма проигрывания музыки записывает своё текущее состояние в статические переменные.
11. Подпрограмма проигрывания музыки отдаёт управление хэндлеру.
12. Хэндлер видит, что список запускаемых процедур закончился и проверяем условие выхода из последовательности запуска подпрограмм.
13. Если условие выхода не соблюдается, то возвращаемся на начало последовательности, т.е. к п.4.
14. Если условие выхода соблюдается, то хэндлер отдаёт управление основной программе.
15. Основная программа чистит за собой память, освобождает ресурсы и завершается.
Как видите, каждая подпрограмма смотрит своё ТЕКУЩЕЕ состояние и выполняет свой ОДИН цикл работы за один раз, а так как посредством хэндлера у нас есть синхронизация действий этих двух подпрограмм, то у конечного пользователя создаётся иллюзия, что обе подпрограммы работают ВМЕСТЕ, т.е. ОДНОВРЕМЕННО показываются картинки и играется музыка.
И всё это без всякой многопоточности, мультизадачности и совершенно не привязано ни к какой операционной системе.
Поставленная задача выполнена!...:)
Успехов!
--------- Стучитесь! И Вас откопают...
Ответ отправил: Sergijj (статус: 4-ый класс)
Ответ отправлен: 26.08.2006, 13:18
Отвечает: InviZ
Здравствуйте, Александр Михайлович!
Да, тогда винды еще не было (спорный вопрос, NT уже была, и многопоточность в ней - тоже)... Каждый программист по-своему реализовывал многопоточность... ) К примеру, обрабатывая прерывание по таймеру (естественно, данная часть писалась на ассемблере). Но в винде никто не позволит так сделать...)
А вообще игры довольно редко используют многопоточность, т.к. разработчики в общем-то не хотят утруждать себя дополнительными проблемами с синхронизацией и т.п.. Чаще всего в отдельном потоке реализуется только звук.
В остальном же все происходит последовательно - к примеру, проверяется ввод пользователя (клавиатурамышьджойстик), затем обновляются объекты сцены в зависимости от действий пользователя и от их стратегии поведения, далее уже происходит отрисовка сцены (это - грубо и упрощенно, конечно, в реальности шагов, естественно, больше, а некоторые действия - например опрос устройств вводавывода могут производиться не каждый кадр, а, к примеру, через каждые 3-5 кадров, для того, чтобы уменьшить потери производительности).
Ответ отправил: InviZ (статус: 2-ой класс)
Ответ отправлен: 26.08.2006, 14:13
Вопрос № 53.368
#include <iostream.h>
int main()
{
int x;
x = 2.3;
cout << x;
}
Заметил такую особенность, прикол в общем такой, если ввести такой
код то, у меня почему то компилятор не ругается на то что должно возвращаться значение из функции, т.е. return 0. В чём прикол? Ошибка разработки компилятора?
Отвечает: InviZ
Здравствуйте, Александр Михайлович!
В данном случае поможет разобраться стандарт. Открываем стандарт, читаем:
A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling exit with the return value as the argument. If control reaches the end of main without encountering a return statement, the effect is that of executing
return 0;
Ответ отправил: InviZ (статус: 2-ой класс)
Ответ отправлен: 26.08.2006, 20:28
Отвечает: Митрофанов Артем Борисович
Здравствуйте, Александр Михайлович!
А прикол в том, что 2.3 - это не int, а float
}
Почему она после компиляции весит аж целых 6,169 кб с копейками
Она же ничего не делает. Объясните. Если с Delphi и его VCL понятно, то в чём здесь прикол тогда.
И ещё по адресам от 0x0000022 до 0x000001ff одни нули. Кому они нужны. Хотя объектный размером всего 307 байт. Спасибо.
Отвечает: InviZ
Здравствуйте, Александр Михайлович!
В основном это из-за того, что программа включает в себя "CRT startup code" - код библиотеки CRT, отвечающий за инициализацию. Обычно это функции mainCRTStartup или WinMainCRTStartup... CRT инициализирует, к примеру, дескрипторы stdin, stdout, инициализирует кучу и т.д. и т.п.
В них происходит вызов функций main или WinMain - определенных в вашей программе, а также выход из программы (к примеру, в Windows - с помощью функции ExitProcess) после возврата из функции main.
В Visual C++ в параметрах линкера можно явно указать, что, скажем, функция main является точкой входа в программу. Если еще отключить присоединение к PE-файла манифеста и отладочной информации, то в результате компиляции такой программы:
Получим exe-файла размером 2 kb, который кроме заголовка PE, кода функции main() и таблицы импорта - с одной функцией ExitProcess, не будет больше содержать ничего.
Ответ отправил: InviZ (статус: 2-ой класс)
Ответ отправлен: 26.08.2006, 21:14