Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Эдвард руки — С

Anna | 24.06.2014 | нет комментариев
Я искал, с чем бы сравнить программирование на С и я припомнил фильм 1990 года режиссера Тима Бертона —«Эдвард руки-ножницы»

Это темная версия Пиноккио, снятая в атмосфере пригорода. В этом фильме чудовищный парень (Джонни Депп) пытается старательно обнять Ванону Райден, но его неуклюжие руки-ножницы делают это дюже опасным для них обоих. Его лицо теснее покрыто большими шрамами.
Если у вас ножницы взамен рук, то это не так уж и нехорошо. У Эдварда много даров: скажем, он может ошеломляюще стричь собак!
Меня Зачастую посещают схожие мысли позже посещения С конференций: в данный раз это было позже Going Native 2013. В прошлом году были фуроры и волнения по поводу нового эталона — С 11. В этом году это было проверкой в реальных условиях. Не осознайте меня ненормально: там было много поражающих собачьих причесок (я имею в виду код на С , тот, что был простым и изящным), но основная часть конференции была о том, как избежать увечий и оказать первую поддержка в случае нечаянной ампутации.

Небольшой магазинчик ужасов.

Там было так много разговоров о том, как не применять С , что это натолкнуло меня на такую мысль: речь идет не о загвоздке некомпетентных программистов, легко применение С — это вообще ненормально. Так, если вы только постигаете основы языка и пытаетесь применять его, то вы обречены.
У С есть оправдание: обратная совместимость, в частности, совместимость с С. Вы можете относиться к С как к подмножеству С , по-настоящему ассемблерному языку, тот, что вам отменнее не применять в повседневном программированию, помимо тех обстановок, когда это очевидно нужно. Если вы слепо погружены в С , то вы думаете о чистых указателях, for-циклах — все это подлинно дурацкая затея.
Отлично знаменитый пример того, как не нужно делать — это применение malloc для динамического выделения памяти и free для ее освобождения. malloc принимает число байт и возвращает указатель на void, тот, что вам нужно кастить во что-то больше комфортное — придумать худшее API для управления памяти трудно. Вот пример подлинно плохого (но примерно правильного, если бы не вероятность обращения по нулевому указателю) кода:

struct Pod {
int count;
int * counters;
};
int n = 10;
Pod * pod = (Pod *) malloc (sizeof Pod);
pod->count = n
pod->counters = (int *) malloc (n * sizeof(int));
...
free (pod->counters);
free (pod);

Верю, что так на С не пишет никто, правда я уверен, что есть много ветхих приложений с такими конструкциями, следственно не нужно хохотать.
С «решил» загвоздку чрезмерно многословного и подверженного ошибкам вычисления размера заменой mallocи free на new и delete. Правильная версия кода выше на С должна выглядеть так:

struct Pod {
int count;
int * counters;
};
int n = 10;
Pod * pod = new Pod;
pod->count = n;
pod->counters = new int [n];
...
delete [] pod->counters;
delete pod;

Кстати, задача обращения по нулевому указателю тоже решилась, потому что new кинет исключение, когда системе не хватает памяти. В коде выше все еще остается маленький шанс утраты памяти, если 2-й вызовnew будет неудачным (Но как Зачастую это случается? Подсказка: насколько огромным может быть n?). Короче, вот по-настоящему правильная версия кода:

class Snd { // Sophisticated New Data (в противовес POD)
public:
Snd (int n) : _count(n), _counters(new int [n]) {}
~Snd () { delete [] _counters; }
private:
int _count;
int * _counters;
};
Snd * snd = new Snd (10);
...
delete snd;

Все ли мы сделали? Безусловно нет! Код не является неопасным в плане исключений.
С рекомендует вам чураться чистых указателей, чураться массивов и чураться delete.
Таким образом, на замену malloc пришел оператор new, тот, что тоже сломан: он возвращает небезопасный указатель, а указатели — это зло.
Все мы знаем (и шхудшее: не только что всякий указатель может указывать на всякий иной указатель, но и что адрес памяти может быть сохранен как целое число либо его младшие биты могут быть использованы как битовые поля (вот отчего для С рассматриваются только консервативные сборщики мусора).
Это распространенное, но ложное суждение, что подсчет ссылок (в частности, применение shared-указателей) отменнее сборки мусора. Современные изыскания показывают, что эти два подхода являются каждого лишь различными сторонами одной медали. Вы обязаны понимать, что удаление shared-указателя может привести к сколь желательно длинным паузам в выполнении программы, равно как и работа сборщика мусора. Это происходит не только потому, что всякий солидный алгорифм подсчета ссылок должен уметь трудиться с циклами, но еще и потому, что всякий раз, когда счетчик ссылок на какую-то часть данных достигает нуля, целый граф указателей, достигаемых из этого объекта, должен быть пройден. Конструкции данных, спроектированные с использованием shared-указателей, могут требовать много времени для удаления и, за исключением примитивных случаев, вы никогда не знаете, какой из указателей выйдет за область видимости и вызовет деструктор.
Опрятное управление источниками и применение shared_ptr все же могут быть обоснованными в однопоточных приложениях, но вы обретаете крупные неприятности, когда начинаете трудиться с многопоточностью. Всякое увеличение и уменьшение счетчика требует блокировки! Эта блокировка обыкновенна реализуется с поддержкой атомарных переменных, но есть еще и мьютексы! Не дозволяйте себя одурачить: доступ к атомарным переменным обходится дорого. Это подводит меня к центральной задаче в С .

Многопоточность и параллелизм

Прошло 8 лет с тех пор, как Герб Саттер браво заявил: «Халява закончилась!»
С тех пор большой танкер С все неторопливей меняет свой курс. Многопоточность не была изобретена в 2005 году. Posix-потоки были сделаны в 1995 году. Майкрософт представила потоки в Windows 95, а поддержку многопроцессорных систем — в Windows NT. Впрочем многопоточность была признана в эталоне С только 2011 года.
С 11 был вынужден начать с больших раздумий. Нужно было определить модель памяти: когда и в каком порядке память, записанная из нескольких потоков, становится видимой из других потоков. Исходя из фактических соображений, модель памяти в С была скопирована с Java (за вычетом некоторых спорных гарантий, тот, что Java дает о поведении в случае рейсов). Короче говоря, программы на С являются ступенчато согласованными, если нет рейсов. Но С доводится соперничать с языком ассемблера, следственно полная модель памяти включает так называемую слабую атомарность, которую я выбираю описывать как переносимые рейсы и рекомендую держаться подальше от нее.
C 11 также определяет примитивы для создания и управления потоками, а также базовые примитивы для синхронизации, определенные Дейкстрой и Хоаром в шестидесятых, такие как мьютексы и условные переменные. Дозволено усомниться в том, что они являются по-настоящему правильной основой для синхронизации, но это не так уж и значимо, чай их все равно невозможно применять в композиции. Компонуемая абстракция для синхронизации — это STM (Software Transactional Memory), которую трудно реализовать правильно и результативно в императивном языке программирования. В Комитете Эталона есть группа постижения STM, так что есть шанс, что в один красивый день STM станет частью эталона. Но STM будет дюже трудно применять надлежащим образом, чай С не предлагает никакого контроля последствий своих действий.
Еще была ложная и запутанная попытка предоставить поддержку task-ориентированного параллелизма сасинхронными задачами и не компонуемыми future (и то, и другое являются серьезными кандидатами в deprecated-список в С 14). Локальные переменные потоков были также стандартизированы исходя из task-ориентированного подхода, тот, что является больше трудным. Блокировки и условные переменные тоже связано с потоками, а не с тасками. Следственно это было абсолютно себе аварией. Комитет Эталона обеспечил себя работой по удалению каждого этого на много лет вперед. Работа включает в себя task-ориентированный компонуемый параллелизм, связь между потоками, Дабы заменить futures (хотелось бы верить), отмену task’ов и, допустимо в дальней перспективе, работающий с данными параллелизм, включаю поддержку GPU.
Производная от Microsoft PPL и Intel TBB должна стать частьюЭталона (верю, что Microsoft AMP там не будет).
Давайте поверим в этом и возможен, что все эти вещи будут стандартизированы и реализованы к, скажем, 2015 году. Даже если это внезапно случится, я все еще не верю, что люди получат вероятность применять С для массового параллельного программирования. С был спроектирован для однопоточного проектирования, а параллельное программирование требует революционных, а не эволюционных изменений. Два слова: data race. Императивные языки не предоставляют охраны от рейсов — может быть, за исключением D.
В С данные по умолчанию расшарены между потоками, являются изменяемыми по умолчанию, и функции имеют побочные результаты также по умолчанию. Все эти указатели и ссылки создают благодатную почву для рейсов, а подверженность конструкций данных и функций рейсам никак не отражается в системе типов. В С , даже если у вас есть константная ссылка на объект, нет никаких гарантий, что иной поток не модифицирует его. Дрянней того, всякие ссылки внутри конст-объектов по умолчанию являются изменяемыми.
D, по крайней мере, имеет представление глубокой константности и неизменности (ни один поток не может изменить постоянную конструкцию данных). Еще один плюс D — это вероятность объявлять чистые функции. Еще в D изменяемые объекты не расшарены между потоками по умолчанию. Это шаг в правильном направлении, правда он и добавляет стоимость выполнения при работе с расшаренными объектами. Правда самое основное: потоки не являются отличной абстракцией для параллельного программирования, следственно такой подход не будет трудиться для легковесных задачи и работой с очередями, когда задачи передаются между потоками.
Но С не поддерживает ничего их этого и не схоже, что когда-нибудь начнет.
Безусловно, вы можете назвать все это про-многопоточностью и фичами параллелизма как функционального программирования — в частности, неизменность и чистые функции.
Но рискну показаться назойливым: Хаскель на голову впереди всех в отношении параллелизма, включая поддержку GPU. Вот отчего я так легко перешел на сторону Хаскеля позже длинных лет отличной евангелистской практики на С . Всякий программист, серьезно относящийся к параллелизму и многопоточности, должен исследовать Хаскель, Дабы осознать его работу с ними. Есть чудесная книга Саймона Марлоу — «Parallel and Concurrent Programming in Haskell». Позже ее прочтения вы либо начнете применять техники функционального программирования при работе с С , либо найдете глобальное несоответствие между параллельным программированием и императивным языком, позже чего переключитесь на Хаскель.

Завершение

Я считаю, что язык С и каждая его философия находятся в прямом раздоре с требованиями функционального программирования. Данный раздор несет ответственность за дюже неторопливое внедрение параллельного программирования в мэйнстримную разработку софта. Мощности многоядерных процессоров, векторные юниты и GPU теряют эффективность из-за устаревших парадигм программирования.

Библиография

Тут я привел несколько из моих публикаций об управлении источниками:

  1. Bartosz Milewski, “Resource Management in C ,” Journal of Object Oriented Programming, March/April 1997, Vol. 10, No 1. p. 14-22. Тут все еще нет unique_ptr, следственно я использую auto_ptr, если это нужно. Я реализовал auto_vector, чай нет вероятности пользоваться вектором auto_ptr.
  2. C Report in September 1998 and February 1999 (auto_ptr еще применяется).
  3. C in Action (still auto_ptr), Addison Wesley 2001.Глядите часть этой книги, в которой говорится оуправлении источниками.
  4. Walking Down Memory Lane, with Andrei Alexandrescu, CUJ October 2005 (применяется unique_ptr)
  5. unique_ptr–How Unique is it?, WordPress, 2009

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB