Как стать автором
Обновить

Комментарии 45

Хотя на Хабре больше популярны скриптовые языки, однако основы — должны знать все ;)


Я тут недавно стал вникать в C-код после большого перерыва и понял, что скриптовые языки потихоньку делают меня дебилом.
#include <stdio.h>

int main()
{
printf(«+1\n»);
return 0;
}
Компилируем код в Dev-C++ 4.9.9.2 и получаем ошибки:

5 E:\...\Dev-Cpp\My projects\test.cpp stray '\171' in program
5 E:\...\Dev-Cpp\My projects\test.cpp stray '\' in program
5 E:\...\Dev-Cpp\My projects\test.cpp expected `)' before «n»
5 E:\...\Dev-Cpp\My projects\test.cpp stray '\187' in program
5 E:\...\Dev-Cpp\My projects\test.cpp invalid conversion from `int' to `const char*'
5 E:\...\Dev-Cpp\My projects\test.cpp initializing argument 1 of `int printf(const char*, ...)'

//вместо таких кавычек: « », надо пользоваться этими '' ''
это ж парсер лох ;)
А я недавно стал писать чуток эмбеддед-С, и мой код стал значительно лучше.
суть не в скриптовых языках а в направлении…
если ты занимаешься поргаммированием бизнес-процессов, то не думаешь об оптимизации выделения памяти при создании-удалении нового объекта…
а если пишешь библиотеку для работы с графикой то тут уже другой вопрос…

вообще просмотрев результаты поиска на хабре понял, что мало кто озадачивался прочтением или хотябы частичным мзучением библии программиста (независимо от того на чем пишет веб2 и веб1, пхп или асм, шарп или джитуее)
Я больше про то, что когда уходишь в скриптовые языки надолго, то как-то деградируешь и ограничиваешься. После этого написать, скажем, модуль для апача или системное приложение будет несколько трудно.

А вот, владея интернет-технологиями, после C писать на, например, php — это как здрасти, только иногда заглядываешь в справочник.

По крайней мере мне так кажется.
глюк
так вот всем бы почитать дональда кнут
искусство программирования… очень прочищает мозги, а задачу про лифты можно решать на любом языке…
en.wikipedia.org/wiki/The_Art_of_Computer_Programming
это не скриптовые языки
Отлична статья, спасибо!
Спасибо! За сегодня это уже вторая статья по системному программированию и эта тенденция меня очень радует.
Очень внятно и структуированно изложено. Спасибо!
Спасибо за статью. Планируете в будущем статью о взаимоблокировках? Почитал бы с удовольствием.
Пожалуйста. Заявку принял. Если кому интересно — оставляйте заявки, буду реализовывать по мере возможности :)
Спасибо. Еще! :)
НЛО прилетело и опубликовало эту надпись здесь
Спасибо! Как глоток свежего воздуха прям.
Если ваша задача требует интенсивного распараллеливания, используйте потоки одного процесса, вместо нескольких процессов. Все потому, что переключение контекста процесса происходит гораздо медленнее, чем контекста потока.

Но если распараллеливаем на несколько ядер, то никаких предписаний насчёт потоков нет, это тоже надо учитывать и не красноглазить, в случае чего :)
Так или иначе, а переключение контекста происходит если не на процессоре #0, так на процессоре #1, однако конечно это не так критически сказывается на производительности.
Четыре ядра и четыре процесса. Что там переключаться будет-то? :)
Далеко не всегда потоки одного процесса выполняется на том же самом процессоре, это уж как планировщик рассудит. В win32 мы его можем насильно заставить, принудительно выставив affinity.

Да и для современных ОС, 4 процесса — очень мало, так что все равно один из процессоров/ядер будет переключатся на выполнение другого процесса, вот тут и смена контекста :)
Гмм. Объясню подробнее. Есть четыре ядра. Есть «тыща» процессов которые ничего не делают, а просто существуют. И есть четыре процесса, которые усиленно что-то считают (однопоточных). И, как альтернатива, у нас есть один процесс, который усиленно что-то считает четырьмя потоками. Если ОС захочет что-то переключить — переключения контекста будут одинаковы в обоих случаях. Если ОС не раскидает четыре считающих процесса по разным ядрам, то автора планировщика нужно казнить.

Я об этом и говорил — не стоит зацикливаться именно на потоках.
Замечания по терминологии

Программный поток (thread): в русскоязычной литературе часто употребляют термин «нить» (в программистском жаргоне — «нитка»).
Fiber: всё же рекомендуется использовать термин «волокно», чтобы отличить его от «нити».

API волокон были опубликованы ещё в Windows NT4, но широкого использования так и не получили — слишком велики оказались затраты для прикладных программистов на проектирование собственного механизма диспетчеризации, в то время, когда использование thread на Win32 ничего не стоило.
Для сравнения: в UNIX создание (fork) нового процесса, в отличие от Win32, ничего не стоит, так что на UNIX thread's (cthreads, pthreads) получили распространение позднее.
ну там же вроде все очень хитро делается. форкнутый процесс это по сути некоторый массив указатель на блоки памяти процесса прародителя и только когда форкнутый хочет что-то поменять в памяти, блок прародителя заменяется на его копию, доступную для редактирования. вроде так как то.
Если позволите некоторые дополнения.
При старте потока в C++ на платформе Windows через CreateThread теряется память и надо использовать _beginthreadex (Windows).
Потоки под Windows (наверное и под Unix тоже) могут иметь свой собственный кусок памяти, доступный только из этого потока (Thread Local Storage).
Если Вы разрабатываете многопоточное приложение, Ваши потоки, как правило, содержат код, отвечающий за передачу управления другим потокам (Sleep под Windows). Иначе 100% загрузки процессора обеспечено.
Не забудьте при компиляции C++ проекта указать, что Вам требуется многопоточная CRT.
Распределение квантов времени по тредам на Линуксе при большой загрузке действительно было отвратительным (неравномерным) на ядре 2.4, сейчас не знаю.

Если Вы можете обойтись без многопоточного программирования — обходитесь! ;-)
> При старте потока в C++ на платформе Windows через CreateThread теряется память и надо использовать _beginthreadex (Windows).

Да нечего не теряется ;) Просто _beginthreadex — это VC++ CRT функция, CreateThread — это win32 api. Первый по сути — это обертка над вторым.

> Если Вы разрабатываете многопоточное приложение, Ваши потоки, как правило, содержат код, отвечающий за передачу управления другим потокам (Sleep под Windows). Иначе 100% загрузки процессора обеспечено.

Как говорится it depends. Зависит от приложения. Даже однопоточное приложение может загрузить процессор на 100% при «должном» подходе :)

С остальным согласен.
support.microsoft.com/kb/104641

Using CreateThread() in a program that uses the CRT (for example, links with LIBCMT.LIB) may cause a memory leak of about 70-80 bytes each time a thread is terminated.

Однопоточное приложение обычно содержит цикл обработки сообщений, в ожидании которых тред и засыпает.
Насколько я понимаю, это происходит потому, что не очищаются статические буферы CRT функций. Т.е. если не использовать внутри функции потока, CRT ф-ции, которые используют статические переменные, то и ликов тоже не будет.
>Если Вы разрабатываете многопоточное приложение, Ваши потоки, как правило, содержат код, отвечающий за передачу управления другим потокам (Sleep под Windows). Иначе 100% загрузки процессора обеспечено.

Замечено, что этот же совет давали разработчики Java 1.0: «используйте Thread.yield()», пока не отошли от устаревшей модели green-threads, в которой сама JVM управляла легковесными программными потоками (например, это было серьёзным препятствием для многозадачных java-приложений на sparc-архитектурах), к модели, в которой используются нити самой операционной системы. Походу пришлось модифицировать методы Thread.suspend(), Thread.resume() и Thread.stop(), чтобы исключить ситуации взаимоблокировки системных потоков — в последствии эти методы объявили «deprecated» (устаревшими и опасными).

В итоге имеем полноценную многозадачность в современной Java2 и J2ME: не нужно более использовать явное указание передачи управления другому потоку Thread.yield(), для разумного управления вычислительными ресурсами в нитях необходимо использовать объекты блокировки (мьютексы или семафоры) и условные переходы в пределах тела исполнения программного потока.

При правильном использовании объектов блокировки в приложении ни один из программных потоков не сможет монопольно завладеть процессором и не захватит 100% его ресурсов. Написание таких приложений, что называется, является «высшим пилотажем» для Java и C/C++ разработчиков, но, например, в Haskel такие приложения сами-собой разумеещиеся.
Не вижу проблемы в использовании Sleep или yield. Иногда потоки не блокируют друг друга, а просто должны параллельно исполняться.
В этом случае ядро отлично справится и без вас. Если не справляется — смените OS. Вроде как последние версии и Linux и Windows справляются…
Ядро может и справится, а вот лишняя нагрузка на процессор нафик не нужна.
Тогда зачем вы педлагаете её создавать? Вызов yield при грамотном планировщике — это «лишняя нагузка на процессор» в чистом виде. Sleep — да, может пригодиться, yield — только при ошибках в проектиовании системы.
В общем да. Но Thread.yield() в современных JVM/OS в принципе не нужен.
А вот для Thread.sleep(int) всё ещё есть применение. ;)
Хорошая статья но есть неточности
Буду очень благодарен, если подскажите. Это поможет мне доработать статью.
>Режим ядра — код, который выполняется в нулевом конце?
может, кольце?
> Если мьютекс просто блокирует поток, до тех пор, пока другой поток не освободит его,
>то cv создают условия, когда потом может заблокировать сам себя
Поток может заблокировать?

За статью спасибо, устаканил некоторые вещи в голове.
Работают на уровне системы, а не на уровне процесса, т.е. могут служить механизмами иннерпроцессной коммуникации (IPC).


Либо интерпроцессной коммуникации, либо межпроцессорного взаимодействия.
Глубочайший респект автору статьи :)
Так забавно вышло, что именно сегодня, когда был опубликован этот топик, мне в учебном заведении рассказывали о потоках. И после прочтения этой статьи, я понял, что рассказывали ине там через пень колоду :)
Очевидно вы программируете под Linux/BSD, так как в статье часто употребляете именно их терминологию. Однако я не стал подобные вещи употреблять, поскольку они очень спорны:

> Thread – это виртуальный процессор

Это не процессор, если хотите это задача планировщика ОС, обеспечивать выполнение потока, создавая для него впечатление, что он выполняется на отдельном процессоре, не гарантируя при этом его выполнение в реальном времени, т.е. поток может останавливаться, прерываться и т.д… Сам по себе поток — это сущность, набор инструкций и данных, что выполняется на процессоре определенную единицу времени.

> Процесс – это в первую очередь адресное пространство.

С этим несогласен. Процессы могут быть разные по своей натуре, например процессы в Erlang. По этому процесс — это в первую очередь сущность, которая инкапсулирует потоки и все остальные вещи связанные с аппаратной защитой памяти и т.д.

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

Да и потоки по своей сути могут быть очень разные. к примеру lightweight потоки реализованные в юзер левел, выполняются по сути на одном физическом процессоре, т.е. не нужно обобщать.
1. Я не привязан к одной системе, нахожу плюсы как в POSIX API, так и в Win 32 API. Что же касается перевода слова «thread» на русский язык как нить, то это скорее влияние OS/2. Собственно, современные threads пришли к нам всем именно оттуда, из OS/2.
Win 32 API это перефразированный и дополненный API OS/2, а POSIX threads возникли как ответ на OS/2 и Win 32 threads — раньше в Unix не было своих threads ни в каком виде вообще, они полностью продинамили эту идею, но затем наверстали упущенное с помощью pthreads.
2. Что касается взгляда на threads как на абстрактные потоки управления — да, я согласен и с таким подходом тоже, Вы правы. Но в Вашей статье очень много привязки все-таки к конкретным современным ОС, а они все именно виртуализируют процессор. Они и прямо в коде своем программном это делают, и концептуально модель именно такова – виртуализируем процессор. Посмотрите, например, на то, что у них представляет собой даже видимый программе контекст thread.
Неплохо. За исключением одного абзаца, в котором искажено всё, что только можно исказить:
Реализация Posix Threads в Linux — Native Posix Threads Library (NPTL). Дело в том, что до версии ядра 2.6 pthreads в Linux был целиком и полностью реализован в режиме пользователя (реализую N: M модель). Однако это противоречило некоторым аспектам стандарта Posix, которые касаются планировщика, сигналов, примитивов синхронизации. Также модель вытеснения потоков, работала во многих случаях с ошибками, по этому поддержку pthread решено было положить на плечи ядра. Сразу две разработки велись в данном направлении компаниями IBM и Red Hat. Однако, реализация Red Hat вышла раньше, в 2003 году, и IBM остановило свою разработку. Позднее NPTL вошли в библиотеку glibc.
1. В Linux никогда не использовалась библиотека с «N: M» моделью или с реализацией потоков в режиме пользователя. И древние LinuxThreads и современная NPTL использовали 1: 1 модель и только 1: 1 модель.
2. IBM разработала NGPT (библиотеку с N: M моделью), но она никогда не была включена ни в один дистрибутив. Есть GNU Pth (N: 1) — но она к Linux не имеет никакого отношения.
3. NGPT вышла раньше, чем NPTL, но так как она никогда не включалась ни в какие дистрибутивы и требовала патчей к ядру, то она благополучно канула в лету — так что сейчас и упоминания-то о ней найти нельзя. Сраните две даты: NGPT 1.0.1 (2001й год) и первая бета версия NPTL (2002й год).

Вот так и рождаются мифы. Поправьте — статья-то в целом хорошая…
Спасибо вам за статью! С интересом жду продолжения. :)
Статья понравилась, хотелось бы слышать про программирование boost::threads (в инете мало документации, или я не там ищу)
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.