Асинхронное программирование — всё по этой теме для программистов


Оглавление (нажмите, чтобы открыть):

Где попрактиковаться в программировании: 30 ресурсов

Быть настоящим программистом — это не только смотреть тысячи часов видео, но и писать десятки тысяч строк кода. Подобрали для вас полезные ресурсы, на которых можно размяться в программировании. Расскажем о них в алфавитном порядке.

C Puzzles

Сайт с головоломками на языке С. Может быть полезен и тем, кто не знает C, но изучает С-подобные языки.

Code Abbey

Множество задач по программированию, рейтинг участников и форум.

CodeChef

Ресурс для регулярных соревнований по программированию. Есть раздел для практики с заданиями разных уровней сложности. Можно потренироваться или выиграть денежный приз.

CodeCombat

Ресурс для начинающих, где обучение построено как игра с возрастающей сложностью. Подойдет изучающим Python, JavaScript или HTML&CSS с нуля.

Codeforces

Русскоязычная платформа для марафонов по программированию. Пользователи делятся на два дивизиона — начинающие соревнуются отдельно от опытных. Можно участвовать в состязаниях или просто тренироваться на задачах в режиме дорешивания.

Codewars

Платформа с разными заданиями — от алгоритмов до шаблонов проектирования — ждет разработчиков, программирующих на Java, JavaScript, PHP, Go, Ruby и других языках. Предварительно надо пройти тестирование.

Coding Bat

Ресурс, посвященный Java и Python. Опытным и начинающим программистам доступны упражнения и справочные материалы по языкам.

CodinGame

Увлекательная практика в формате видеоигр. Поддерживаются 25 языков: Java, JavaScript, PHP, Python, Swift, C#, C++, Ruby и другие.

Empire of Code

Русскоязычный ресурс с задачами по Python и JavaScript в формате игры. Участники пишут код для стратегии и тактики персонажей.

Exercism

Сайт предлагает задачи на 48 языках программирования. Пользователь скачивает упражнения, решает их на собственном компьютере, а затем проверяет с наставником. Например, в разделе Python 111 упражнений и 70 менторов, его изучают 29 тысяч пользователей, а в разделе PHP — 64 упражнения, 14 наставников и 4 тысячи студентов.

HackerRank

Платформа для опытных кодеров. Создатели приглашают присоединиться к 5 миллионам пользователей сайта, чтобы тренироваться, готовиться к собеседованиям и искать работу.

InterviewBit

Сайт помогает подготовиться к интервью в Google, Facebook, Microsoft и других корпорациях и получить оффер.

LeetCode

Ресурс с заданиями для подготовки к техническим интервью в крупных компаниях. На сайте почти тысяча вопросов, поддерживаются 14 языков программирования.

MAXimal

Русскоязычный ресурс для освоения алгоритмов. Есть подробные описания и упражнения. Бонус — большая библиотека по программированию. Сайт не обновляется.

MindCipher

Ресурс позиционируется как социальный репозиторий с самыми занимательными головоломками.

Programming Praxis

Блог, в котором еженедельно размещают новые интересные задачи, чтобы программисты тренировали навыки и мозги.

Programming Skills

Сайт с онлайн-тестами и вопросами для технического интервью. Подойдет тем, кто работает с HTML, JavaScript, C#, Java, PHP, C# и другими ЯП.

Programmr

Платформа, на которой собраны задачки по Java, PHP, Python, C# и Ruby. Ресурс давно не обновляется, но потренироваться еще можно.

Project Euler

Сайт с непростыми заданиями для математиков и программистов. Задачи ранжируются по сложности.

Prolog Problems

На сайте программиста Вернера Хетта вы найдете краткий курс по языку Prolog и сборник упражнений для тренировки. Ресурс не обновляется.

PythonChallange

Сайт для тех, кто хочет освоить Python. Чтобы пройти многоуровневую игру Challenge, надо создавать небольшие программы. Сейчас в ней 33 уровня сложности.

Rosalind

Ресурс по изучению биоинформатики. Есть обучающий курс по Python.

Ruby Quiz

Еженедельная викторина по Ruby, которая приходит в рассылке. Автор ресурса отошел от дел, но инициативу продолжают энтузиасты. Можно подписаться на письма или порешать задачки из архивных выпусков.

На платформе Sphere Online Judge зарегистрированы 650 тысяч пользователей и размещено более шести тысяч заданий. Ресурс поддерживает 45 языков программирования, в том числе C, C++, Pascal, Perl, Haskell, Ocaml и другие.

SQL-EX.RU

Кладезь знаний для тех, кто изучает SQL. Множество упражнений для отработки навыков и рейтинг участников.

Timus Online Judge

Ресурс Уральского федерального университета, на котором собран архив задач по программированию с автоматической проверкой.

Topcoder

Платформа, на которой проводятся соревнования по программированию. Можно потренироваться, выиграть денежный приз, а бонусом получить интересный оффер.

W3Resource

Портал предлагает огромное количество обучающих материалов по веб-разработке, базам данных, Linux и даже программам Excel или шаблонам Google Forms. Есть упражнения и квизы по базам данных, PHP, JavaScript, Java, Swift и другим языкам.

Питонтьютор

Русскоязычный ресурс для новичков в Python. Это интерактивный учебник с задачками, которые решаются без установки среды, прямо в браузере.

Школа программиста

Ресурс, который создали во Дворце пионеров в Красноярске. Он предлагает множество задач и разбирает их для начинающих программистов.

А вы где практикуетесь? Поделитесь в комментариях.

Быть настоящим программистом — это не только смотреть тысячи часов видео, но и писать десятки тысяч строк кода. Подобрали для вас полезные ресурсы, на которых можно размяться в программировании. Расскажем о них в алфавитном порядке.

C Puzzles

Сайт с головоломками на языке С. Может быть полезен и тем, кто не знает C, но изучает С-подобные языки.

Code Abbey

Множество задач по программированию, рейтинг участников и форум.

CodeChef

Ресурс для регулярных соревнований по программированию. Есть раздел для практики с заданиями разных уровней сложности. Можно потренироваться или выиграть денежный приз.

CodeCombat

Ресурс для начинающих, где обучение построено как игра с возрастающей сложностью. Подойдет изучающим Python, JavaScript или HTML&CSS с нуля.

Codeforces

Русскоязычная платформа для марафонов по программированию. Пользователи делятся на два дивизиона — начинающие соревнуются отдельно от опытных. Можно участвовать в состязаниях или просто тренироваться на задачах в режиме дорешивания.

Codewars

Платформа с разными заданиями — от алгоритмов до шаблонов проектирования — ждет разработчиков, программирующих на Java, JavaScript, PHP, Go, Ruby и других языках. Предварительно надо пройти тестирование.

Coding Bat

Ресурс, посвященный Java и Python. Опытным и начинающим программистам доступны упражнения и справочные материалы по языкам.

CodinGame

Увлекательная практика в формате видеоигр. Поддерживаются 25 языков: Java, JavaScript, PHP, Python, Swift, C#, C++, Ruby и другие.

Empire of Code

Русскоязычный ресурс с задачами по Python и JavaScript в формате игры. Участники пишут код для стратегии и тактики персонажей.

Exercism

Сайт предлагает задачи на 48 языках программирования. Пользователь скачивает упражнения, решает их на собственном компьютере, а затем проверяет с наставником. Например, в разделе Python 111 упражнений и 70 менторов, его изучают 29 тысяч пользователей, а в разделе PHP — 64 упражнения, 14 наставников и 4 тысячи студентов.


HackerRank

Платформа для опытных кодеров. Создатели приглашают присоединиться к 5 миллионам пользователей сайта, чтобы тренироваться, готовиться к собеседованиям и искать работу.

InterviewBit

Сайт помогает подготовиться к интервью в Google, Facebook, Microsoft и других корпорациях и получить оффер.

LeetCode

Ресурс с заданиями для подготовки к техническим интервью в крупных компаниях. На сайте почти тысяча вопросов, поддерживаются 14 языков программирования.

MAXimal

Русскоязычный ресурс для освоения алгоритмов. Есть подробные описания и упражнения. Бонус — большая библиотека по программированию. Сайт не обновляется.

MindCipher

Ресурс позиционируется как социальный репозиторий с самыми занимательными головоломками.

Programming Praxis

Блог, в котором еженедельно размещают новые интересные задачи, чтобы программисты тренировали навыки и мозги.

Programming Skills

Сайт с онлайн-тестами и вопросами для технического интервью. Подойдет тем, кто работает с HTML, JavaScript, C#, Java, PHP, C# и другими ЯП.

Programmr

Платформа, на которой собраны задачки по Java, PHP, Python, C# и Ruby. Ресурс давно не обновляется, но потренироваться еще можно.

Project Euler

Сайт с непростыми заданиями для математиков и программистов. Задачи ранжируются по сложности.

Prolog Problems

На сайте программиста Вернера Хетта вы найдете краткий курс по языку Prolog и сборник упражнений для тренировки. Ресурс не обновляется.

PythonChallange

Сайт для тех, кто хочет освоить Python. Чтобы пройти многоуровневую игру Challenge, надо создавать небольшие программы. Сейчас в ней 33 уровня сложности.

Rosalind

Ресурс по изучению биоинформатики. Есть обучающий курс по Python.

Ruby Quiz

Еженедельная викторина по Ruby, которая приходит в рассылке. Автор ресурса отошел от дел, но инициативу продолжают энтузиасты. Можно подписаться на письма или порешать задачки из архивных выпусков.

На платформе Sphere Online Judge зарегистрированы 650 тысяч пользователей и размещено более шести тысяч заданий. Ресурс поддерживает 45 языков программирования, в том числе C, C++, Pascal, Perl, Haskell, Ocaml и другие.

SQL-EX.RU

Кладезь знаний для тех, кто изучает SQL. Множество упражнений для отработки навыков и рейтинг участников.

Timus Online Judge

Ресурс Уральского федерального университета, на котором собран архив задач по программированию с автоматической проверкой.

Topcoder

Платформа, на которой проводятся соревнования по программированию. Можно потренироваться, выиграть денежный приз, а бонусом получить интересный оффер.

W3Resource

Портал предлагает огромное количество обучающих материалов по веб-разработке, базам данных, Linux и даже программам Excel или шаблонам Google Forms. Есть упражнения и квизы по базам данных, PHP, JavaScript, Java, Swift и другим языкам.

Питонтьютор

Русскоязычный ресурс для новичков в Python. Это интерактивный учебник с задачками, которые решаются без установки среды, прямо в браузере.

Школа программиста

Ресурс, который создали во Дворце пионеров в Красноярске. Он предлагает множество задач и разбирает их для начинающих программистов.

А вы где практикуетесь? Поделитесь в комментариях.

Шаблоны асинхронного программирования Asynchronous programming patterns

В .NET есть три шаблона для выполнения асинхронных операций: .NET provides three patterns for performing asynchronous operations:

Асинхронный шаблон на основе задач (TAP) , который использует один метод для запуска и завершения асинхронной операции. Task-based Asynchronous Pattern (TAP), which uses a single method to represent the initiation and completion of an asynchronous operation. Шаблон TAP был реализован в .NET Framework 4. TAP was introduced in the .NET Framework 4. Именно его рекомендуется использовать для асинхронного программирования в .NET. It’s the recommended approach to asynchronous programming in .NET. Ключевые слова async и await в C#, а также операторы Async и Await в Visual Basic добавляют для TAP поддержку языка. The async and await keywords in C# and the Async and Await operators in Visual Basic add language support for TAP. Дополнительные сведения см. в разделе Асинхронный шаблон, основанный на задачах (TAP). For more information, see Task-based Asynchronous Pattern (TAP).

Асинхронная модель на основе событий (EAP) . Это устаревшая модель на основе событий, обеспечивающая работу в асинхронном режиме. Event-based Asynchronous Pattern (EAP), which is the event-based legacy model for providing asynchronous behavior. Для нее требуется метод с суффиксом Async , одно или несколько событий, типы делегатов обработчика событий и производные типы EventArg . It requires a method that has the Async suffix and one or more events, event handler delegate types, and EventArg -derived types. Протокол EAP был введен в платформа.NET Framework 2.0. EAP was introduced in the .NET Framework 2.0. Ее не рекомендуется использовать для новых разработок. It’s no longer recommended for new development. Дополнительные сведения см. в разделе Асинхронная модель на основе событий (EAP). For more information, see Event-based Asynchronous Pattern (EAP).

Модель асинхронного программирования (APM) (также называется шаблоном IAsyncResult). Это устаревшая модель, в которой для реализации асинхронного поведения используется интерфейс IAsyncResult. Asynchronous Programming Model (APM) pattern (also called the IAsyncResult pattern), which is the legacy model that uses the IAsyncResult interface to provide asynchronous behavior. В этом шаблоне для синхронных операций требуются методы Begin и End (например, BeginWrite и EndWrite позволяют реализовать асинхронную операцию записи). In this pattern, synchronous operations require Begin and End methods (for example, BeginWrite and EndWrite to implement an asynchronous write operation). Этот шаблон не рекомендуется использовать для разработки новых приложений. This pattern is no longer recommended for new development. Дополнительные сведения см. в статье Асинхронная модель программирования (APM). For more information, see Asynchronous Programming Model (APM).

Сравнение шаблонов Comparison of patterns

Для быстрого сравнения, как с помощью трех шаблонов моделировать асинхронные операции, рассмотрим метод Read , который считывает указанный объем данных в предоставленный буфер, начиная с заданного смещения: For a quick comparison of how the three patterns model asynchronous operations, consider a Read method that reads a specified amount of data into a provided buffer starting at a specified offset:

Этот же метод с использованием TAP предоставит такой метод ReadAsync : The TAP counterpart of this method would expose the following single ReadAsync method:

Аналог EAP будут предоставлять следующий набор типов и членов: The EAP counterpart would expose the following set of types and members:

Этот же метод с APM предоставит методы BeginRead и EndRead : The APM counterpart would expose the BeginRead and EndRead methods:

Понимание асинхронного программирования

Node Hero: Глава 3

Перевод книги Node Hero от RisingStack . Переведено с разрешения правообладателей.

В этой главе я расскажу вам о принципах асинхронного программирования и покажу, как создавать асинхронные операции в JavaScript и Node.js.

Синхронное программирование

В традиционной практике программирования большинство операций ввода-вывода происходит синхронно. Если вы задумайтесь о Java и о том, как вы читаете файл в ней, вы получите что-то вроде этого:

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

Если у вас больше блокирующих операций, очередь событий становится ещё хуже:

Для решения этой проблемы Node.js предлагает модель асинхронного программирования.

Асинхронное программирование в Node.js

Асинхронный ввод-вывод — это форма обработки ввода/вывода, позволяющая продолжить обработку других задач, не ожидая завершения передачи.

В следующем примере я покажу простой процесс чтения файлов в Node.js, как синхронным, так и асинхронным способом, с целью показать вам, чего вы можете достигнуть, если будете избегать блокировки ваших приложений.

Начнем с простого примера: синхронное чтение файла с использованием Node.js:

Что здесь происходит? Мы читаем файл, используя синхронный интерфейс модуля fs . Он работает ожидаемым образом: в переменную content сохраняется содержимое file.md . Проблема с этим подходом заключается в том, что Node.js будет заблокирована до завершения операции, то есть, пока читается файл, она не может сделать ничего полезного.

Посмотрим, как мы можем это исправить!

Асинхронное программирование, в том виде, в каком мы знаем его в JavaScript, может быть реализовано только при условии, что функции являются объектами первого класса: они могут передаваться как любые другие переменные другим функциям. Функции, которые могут принимать другие функции в качестве аргументов, называются функциями высшего порядка.

Один из самых простых примеров функций высшего порядка:

В приведенном выше примере мы передаем функцию isBiggerThanTwo в функцию filter . Таким образом, мы можем определить логику фильтрации.

Так появились функции обратного вызова ( колбеки): если вы передаете функцию другой функции в качестве параметра, вы можете вызвать её внутри функции, когда она закончит свою работу. Нет необходимости возвращать значения, нужно только вызывать другую функцию с этими значениями.

В основе Node.js лежит принцип «первым аргументом в колбеке должна быть ошибка». Его придерживаются базовые модули, а также большинство модулей, найденных в NPM.

Что следует здесь выделить:

  • обработка ошибок: вместо блока try-catch вы проверяете ошибку в колбеке
  • отсутствует возвращаемое значение: асинхронные функции не возвращают значения, но значения будут переданы в колбеки

Давайте немного изменим этот файл, чтобы увидеть, как это работает на практике:

Результатом выполнения этого кода будет:

Как вы можете видеть, как только мы начали читать наш файл, выполнение кода продолжилось, а приложение вывело end of the file . Наш колбек вызвался только после завершения чтения файла. Как такое возможно? Встречайте цикл событий ( event loop).

Цикл событий

Цикл событий лежит в основе Node.js и JavaScript и отвечает за планирование асинхронных операций.

Прежде чем погрузиться глубже, давайте убедимся, что мы понимаем, что такое программирование с управлением по событиям ( event-driven programming).

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

На практике это означает, что приложения реагируют на события.

Кроме того, как мы уже узнали в первой главе, с точки зрения разработчика Node.js является однопоточным. Это означает, что вам не нужно иметь дело с потоками и синхронизировать их, Node.js скрывает эту сложность за абстракцией. Всё, кроме кода, выполняется параллельно.

Для более глубокого понимания работы цикла событий рекомендуем посмотреть это видео:


Асинхронный поток управления

Поскольку теперь у вас есть общее представление о том, как работает асинхронное программирование в JavaScript, давайте рассмотрим несколько примеров того, как вы можете организовать свой код.

Async.js

Чтобы избежать так называемого Callback-Hell, вы можете начать использовать async.js.

Async.js помогает структурировать ваши приложения и упрощает понимание потока управления.

Давайте рассмотрим короткий пример использования Async.js, а затем перепишем его с помощью промисов.

Следующий фрагмент перебирает три файла и выводит системную информацию по каждому:

Примечание переводчика: если вы пользуетесь Node.js версии 7 и выше, лучше воспользоваться встроенными конструкциями языка, такими как async/await.

Промисы

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

На практике предыдущий пример можно переписать следующим образом:

Конечно, если вы используете метод, возвращающий промис, то пример будет заметно компактнее.

Какой язык программирования стоит выучить первым ?

Введение

«Новичку в сфере IT стоит знать одно простое правило программирования — это написание очень тонких инструкций для глупой, но послушной машины».

Среди массы различных языков программирования новичку в IT очень сложно выбрать направления для дальнейшего развития, потому что каждый язык занимает определенную ячейку. Наиболее популярные, языки в «современном программировании», это: Java, Python, Objective-C, PHP, C, C++, C#, JavaScript и Ruby. Форумы и специализированные сайты переполнены тематикой «Что выбрать?» и «Куда развиваться?». И мы нашли оптимальную схему выбора ветки развития юным программистам.

С чего начать изучение программирования?

Итак, с чего же стоит начать? В первую очередь ответьте предельно честно самому себе на главный вопрос: «Почему Вы хотите начать изучать программирование?». Ответов много, а предпосылок еще больше, но если провести анализ, то мотивов стать разработчиками несколько.

  • Деньги
    Часто, наиболее распространенной причиной изучения программирования служит мнение о том, что программисты много зарабатывают. Да, это действительно так. Зарплаты хороших программистов могут заставить позавидовать даже некоторых менеджеров высшего звена. Но это достигается годами упорного труда, само мотивации и углубления в современные информационные технологии. Кроме того, дабы получать по-настоящему достойную зарплату, стоит сразу нацеливается на работу в хорошей крупной компании. Или же иметь идею на миллион и открывать свой start-up.
  • Перспективы
    Казалось бы, относительно недавно стоило появится первом персональному компьютеру – и нате Вам! – программисты стали одними из наиболее востребованных работниками современного рынка. Действительно, наш век – это век информационных технологий, быстроразвивающийся и динамический. Сейчас спрос на специалистов IT-сферы растет как никогда, и вряд ли стоит ждать на него спад. Все больше и больше современных компаний готовы предложить «теплое местечко». Сколько их – Google, Microsoft, EPAM прочие.
  • Динамичность
    Каждому человеку присуща толика любознательности. Но есть люди, у которых страсть изучать что-то новое просто в крови! Что же, в таком случае программирование – это именно то, что вам нужно. На данный момент существуют десятки различных направлений: мобильная разработка, настольные приложения, веб-программирование, игры, сколько их… А уж сколько для этих направлений было разработано технологий и программ – не счесть. Работая в сфере IT вы можете быть уверенным, что никогда не дадите себе заскучать!
    Итак, раз мы разобрались с нашими мотивами и разложили все по «полочкам», приступим же непосредственно к выбору первого языка программирования!

Какой язык программирования выбрать?

Теперь, самый важный вопрос: так какой же язык программирования стоит выбрать новичку в сфере IT? Прежде всего, все зависит от того, что именно вы желаете изучать.

Вот график наиболее востребованных направлений области информационных технологий:

Как мы можем видеть, список внушительный. Но что из этого вас прельщает больше всего? Давайте попробуем разобраться.

  • Я бы хотел разрабатывать сайты
    С этим направлением не все так просто. Есть Front-End разработка сайтов – это все, что вы видите на экране. Создание веб-страниц, программ для них, стилей и много чего прочего. В таком случае вам стоит обратить свое внимание на JavaScript и HTML & CSS. А есть Back-End – разработка непосредственно программ для серверов – тех алгоритмов, которые, собственно говоря, и будут управлять страницами, сайтом и прочим. Здесь все несколько сложнее, так как сразу же появляется хороший выбор из Python, Java, C#, и PHP. Каждый из этих языков обладает как серией достоинств, так и набором откровенных недостатков. В качестве этакого старта советуем рассмотреть Python.
  • Настольные приложения для домашних ПК
    Тут бесспорными лидерами выступают такие популярные языки, как Java и C#. С одной стороны, обучение Java несколько проще и быстрее, чем C#, с другой, набор возможностей, которыми может похвалится C#, на порядок выше.
  • Работа с базами данных
    Ну, тут все однозначно: следует начинать с SQL! Администрирование, работа с реляционными базами данными и прочее, что так необходимо в современно IT-мире. Здесь можно рассмотреть вступление в язык запросов.
  • Игры-игры-игры!
    Геймерами не стают, ими рождаются. Наслаждаетесь современными продуктами игровой индустрии и сами бы хотели привнести что-то в этой увлекательный виртуальный мир? Тогда, определенно, вам стоит обратить свое внимание на С++.

Здесь можно посмотреть статистику языков программирования по популярности:

Статистика поиска соискателей по языкам и платформам:

Какой самый простой язык программирования?

Если начинать изучать, что начинать с простого, не так ли? Итак, вот небольшой даждест «для чайников» с чего, собственно, стоит начать обучение.

  • JavaScript
    Один из наиболее легких языков программирования для веб-разработки. Динамическое наполнение web HTML страниц, целый комплекс технологий семейства React.JS, Node.JS и прочих, обилие библиотек и обучающих материалов + плюс, огромное комьюнити. Отличный старт для новичка в области IT! Вот вступление в видео-курс по JavaScript.
  • Python
    Очень легкий и приятный в освоении интерпретируемый язык универсального назначения. Обширное количество довольных пользователей, огромное количество вакансий на мировом рынке, поддержка большинством сред разработки и наличие специализированных сред разработки. Ознакомится с данным языком можно здесь.
  • PHP
    PHP – или Hypertext Preprocessor – достаточно мощный и, в то же время, легкий в освоении язык программирования для разработки серверной части веб-сайта. Имеет богатую историю и может похвалится хорошей базой различных библиотек. Ознакомится с ним вы можете здесь.

Самый сложный язык программирования

Быть может, вы уже имели определенный опыт, связанный с IT, и желаете попробовать себя в более «продвинутой» песочнице? Или вы не из тех, кто хочет изучать что-то простое, а сразу брать «быка за рога»? Что же, на этот случай мы можем предложить следующее:

  • Java
    Отменный компилируемый язык программирования для решения целого спектра задач – от написания настольных приложений до создания серверных программ для веб-сайтов. Имеет мощную аудиторию поклонников, богатую историю, корни которой простираются в самые 90-стые и множество библиотек для практически любой задачи. Начать изучение этого языка можно здесь.
  • С#
    Язык программирования от компании Microsoft универсального назначения. Java показалась простой? Тогда C# для вас! Имеет практически идентичный с Java синтаксис, но отличается расширенном набором функций и производимых операций. WPF для оконных приложений, ASP.NET для разработки сайтов и прочие высококачественные фреймворки для всех типов задач. Что может быть лучше, чем инструмент на все случаи жизни? Приступить к работе с этим языком можно здесь.
  • C++
    Легенда программирования. Универсальный язык программирования, возможности которого воистину не имеют ограничений. Низкоуровневая работа с памятью, разработка систем рендеринга для игр, отличная производительность и неисчерпаемый набор библиотек… С++ — это наиболее быстрый и оптимизированный язык программирования из всех существующих. Но его изучение потребует особенной усидчивости и трудолюбия. Сумеете ли вы с ним совладать?

Языки программирования, какой самый оплачиваемый?

Один из самых востребованных и высокооплачиваемых на рынке языков программирования — это Java. Очень популярен на всех платформах, ОС и устройствах, благодаря своей кроссплатформенности. Используется в Gmail, Minecraft, большинстве Android приложений и в корпоративных приложениях.

  • С — это «лингва франка» среди всех языков программирования. Один из самых старых и самых широко используемых языков в мире. Отлично подходит для системного и аппаратного программирования. Он используется в ОС и оборудовании.
  • С# был создан на платформе Microsoft, но совсем недавно вышел на open source. С# — это популярный выбор предприятий для разработки разнообразных web-сайтов и Windows приложений, используя .NET framework. С# используют для создания web сайтов при помощи web фреймворка от Microsoft – ASP.NET. Своим синтаксисом и функционалом похож на Java. Используется в корпоративных и Windows приложениях.
  • Objective-C является основным языком, используемый Apple для Mac OS X и iOS. Его стоит изучать, если Вы собираетесь разрабатывать только под OS X и iOS. Стоит задуматься над изучением Swift, как о следующем языке. Objective-C используется в большинстве iOS приложений и в части Mac OS X.
  • C++ — это более сложная версия языка программирования С, с существенно расширенным набором возможностей. Широко используется при разработке игр, промышленных и высокопроизводительных приложений. Изучать С++ — все равно что изучать, как производить, собирать и водить машину. Этот язык не рекомендуется для самостоятельного изучения и требует наличие ментора. Он широко используется в ОС, оборудовании и браузерах.

На самом деле, абсолютно не важно, с чего именно Вы начнете Ваш путь в сфере IT. Нужно знать хотя бы несколько основных языков и технологий, чтобы познать все аспекты программирования. А самое главное — начать!

Асинхронное программирование — всё по этой теме для программистов

Контекст синхронизации (SynchronizationContext) — позволяет возобновить выполнение метода в конкретном потоке.
Текущий контекст SynchronizationContext – это свойство текущего потока. Идея в том, что всякий раз, как код исполняется в каком-то специальном потоке, мы
можем получить текущий контекст синхронизации и сохранить его.
Впоследствии этот контекст можно использовать для того, чтобы продолжить исполнение кода в том потоке, в котором оно было начато.

В классе SynchronizationContext есть важный метод Post , который гарантирует, что переданный делегат будет исполняться в правильном контексте.

Важно! В момент приостановки метода при встрече оператора await текущий контекст SynchronizationContext сохраняется.
Далее, когда метод возобновляется, компилятор вставляет вызов Post , чтобы исполнение возобновилось в запомненном контексте.

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

К числу таких длительных операций можно отнести:
• сетевые запросы;
• доступ к диску;
• продолжительные задержки.

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

Мастер Йода рекомендует:  Kotlin — всё по этой теме для программистов

Всякий раз, запуская новый поток или пользуясь классом ThreadPool, вы пишете асинхронную программу , потому что текущий поток может продолжать заниматься другими вещами.

Асинхронный код освобождает поток , из которого был запущен. И это очень хорошо по многим причинам.

Часто существует лишь один поток, способный выполнить определенную задачу (например, поток пользовательского интерфейса) и, если не освободить его быстро, то приложение перестанет реагировать на действия пользователя.

Но самым важным мне кажется тот факт, что асинхронное выполнение открывает возможность для параллельных вычислений .

В версии C# 5.0 Microsoft добавила механизм, предстающий в виде двух новых ключевых слов: async и await .

Механизм async встроен в компилятор и без поддержки с его стороны не мог бы быть реализован в библиотеке. Компилятор преобразовывает исходный код, то есть действует примерно по тому же принципу, что в случае лямбда-выражений и итераторов в предыдущих версиях C#.

У ASP.NET-приложений на веб-сервере нет ограничения на единственный поток , как в случае программ с пользовательским интерфейсом. И тем не менее асинхронное выполнение может оказаться весьма полезным, так как для таких приложений характерны длительные операции, особенно запросы к базе данных.

В зависимости от версии IIS может быть ограничено либо общее число потоков, обслуживающих веб-запросы, либо общее число одновременно обрабатываемых запросов. Если большая часть времени обработки запроса уходит на обращение к базе данных, то увеличение числа одновременно обрабатываемых запросов может повысить пропускную способность сервера

Помните: основная отличительная особенность асинхронного кода состоит в том, что поток, начавший длительную операцию, освобождается для других дел .
В случае ASP.NET этот поток берется из пула потоков, поэтому после запуска длительной операции он сразу же возвращается в пул и может затем использоваться для обработки других запросов. Таким образом, для обработки одного и того же количества запросов требуется меньше потоков.

Библиотека Task Parallel Library была включена в версию .NET Framework 4.0. Важнейшим в ней является класс Task , представляющий выполняемую операцию. Его универсальный вариант, Task , играет роль обещания вернуть значение (типа T), когда в будущем, по завершении операции, оно станет доступно.

Как мы увидим ниже, механизм async в C# 5.0 активно пользуется классом Task. Но и без async классом Task, а особенно его вариантом Task , можно воспользоваться при написании асинхронных программ. Для этого нужно запустить операцию, которая возвращает Task , а затем вызвать метод ContinueWith для регистрации обратного вызова.
Это называется асинхронность вручную потому что используем метод ContinueWith .

Заметка на будующее:
Класс Task умеет в частности обрабатывать исключения и работать с контекстами синхронизации SynchronizationContext .
Мы увидим, что это полезно, когда требуется выполнить обратный вызов в конкретном потоке (например, в потоке пользовательского интерфейса).

1-ый шаг это пометка метода ключевым словом async .
Оно включается в сигнатуру метода точно так же, как, например, слово static.

2-ой шаг мы должны дождаться завершения скачивания, воспользовавшись ключевым словом await .
С точки зрения синтаксиса C#, await – это унарный оператор, такой же как оператор ! или оператор приведения типа (type).
Он располагается слева от выражения и означает, что нужно дождаться завершения асинхронного выполнения этого выражения.

На заметку!
Метод, помеченный ключевым словом async, автоматически не становится асинхронным .
Async-методы лишь упрощают использование других асинхронных методов. Они начинают исполняться синхронно, и так происходит до тех пор, пока не встретится вызов асинхронного метода внутри оператора await .
В этот момент сам вызывающий метод становится асинхронным.
Если же оператор await не встретится, то метод так и будет выполняться синхронно до своего завершения.

Я говорил, что класс Task представляет выполняемую операцию, а его подкласс Task – операцию, которая в будущем вернет значение типа T. Можно считать, что Task – это обещание вернуть значение типа T по завершении длительной операции.
Оба класса Task и Task могут представлять асинхронные операции, и оба умеют вызывать ваш код по завершении операции. Чтобы воспользоваться этой возможностью вручную, необходимо вызвать метод ContinueWith , передав ему код, который должен быть выполнен, когда длительная операция завершится. Именно так и поступает оператор await , чтобы выполнить оставшуюся часть async-метода .

Если применить await к объекту типа Task , то мы получим выражение await , которое само имеет тип T .
Это означает, что результат оператора await можно присвоить переменной, которая используется далее в методе, что мы и видели в примерах.
Однако если await применяется к объекту неуниверсального класса Task , то получается предложение await , которое ничему нельзя присвоить (как и результат метода типа vo >Task не обещает вернуть значение в качестве результата, а представляет лишь саму операцию.

Метод, помеченный ключевым словом async , может возвращать значения трех типов:
• void
• Task
• Task , где T – некоторый тип.

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

Я провожу различие между типом возвращаемого методом значения (например, Task ) и типом результата, который нужен вызывающей программе (в данном случае string).
В обычных, не асинхронных, методах тип возвращаемого значения совпадает с типом результата, тогда как в асинхронных методах они различны – и это очень существенно.

Использовать тип void следует, когда вы уверены, что вызывающей программе безразлично, когда завершится операция и завершится ли она успешно.

Async-методы , возвращающие тип Task , позволяют вызывающей программе ждать завершения асинхронной операции и распространяют исключения, имевшие место при ее выполнении.

Если значение результата несущественно, то метод типа async Task предпочтительнее метода типа async void , потому что вызывающая программа получает возможность узнать о завершении операции, что упрощает упорядочение задач и обработку исключений.

Ключевое слово async указывается в объявлении метода, как public или static.
Однако спецификатор async не считается частью сигнатуры метода, когда речь заходит о переопределении виртуальных методов, реализации интерфейса или вызове.
То есть в отношении переопределения методов и реализации интерфейсов, слово async полностью игнорируется.

В объявлениях методов интерфейса слово async запрещено просто потому, что в этом нет необходимости. Если в интерфейсе объявлен метод, возвращающий тип Task , то в реализующем интерфейс классе этот метод может быть помечен словом async, а может быть и не помечен – на усмотрение программиста.

Когда поток исполнения программы доходит до оператора await, должны произойти две вещи:

1) Текущий поток должен быть освобожден, чтобы поведение программы было асинхронным. С привычной, синхронной, точки зрения это означает, что метод должен вернуть управление.

2) Когда задача Task , погруженная в оператор await, завершится, ваш метод должен продолжить выполнение с того места, где перед этим вернул управление, как будто этого возврата никогда не было.

Чтобы добиться такого поведения, метод должен приостановить выполнение, дойдя до await , и возобновить его впоследствии.

Чтобы стало яснее, сколько работы должен выполнить компилятор C#, встретив в программе оператор await , я перечислю, какие именно аспекты состояния метода необходимо сохранить.

Во-первых, запоминаются все локальные переменные метода, в том числе:
• параметры метода
• все переменные, определенные в текущей области видимости
• все прочие переменные, например счетчики циклов
• переменную this, если метод не статический

В результате после возобновления метода окажутся доступны все переменные-члены класса.
Всё это сохраняется в виде объекта в куче .NET, обслуживаемой сборщиком мусора. Таким образом, встретив await , компилятор выделяет память для объекта, то есть расходует ресурсы, но в большинстве случае это не приводит к потере производительности.
C# также запоминает место, где встретился оператор await .

контекст синхронизации, который среди прочего позволяет возобновить выполнение метода в конкретном потоке.
мое: Task помогает востанавливать контекст синхронизации, перед обратным вызовом

ExecutionContext — это родительский контекст, включающий все остальные контексты. Он не имеет собственного поведения, а служит только для запоминания и передачи контекста и используется такими компонентами .NET, как класс Task.


Оператор await можно использовать почти в любом месте метода, помеченного ключевым словом async .

Оператор await может встречаться внутри блока try , но не внутри блоков catch или finally .

В блоке catch часто, а в блоке finally всегда, исключение еще находится в фазе раскрутки стека и позже может быть возбуждено в блоке повторно.

Если использовать в этой точке await, то стек окажется другим, и определить в этой ситуации поведение повторного возбуждения исключения было бы очень сложно.
Напомню, что await всегда можно поставить не внутри блока catch, а после него, для чего следует либо воспользоваться предложением return, либо завести булевскую переменную, в которой запомнить, возбуждала ли исходная операция исключение. Например, вместо такого некорректного в C# кода:

можно было бы написать:

bool failed = false;

try
<
page = await webClient.DownloadStringTaskAsync( «https://aaa.com» );
>
catch (WebException)
<
failed = true;
>

if (failed)
<
page = await webClient.DownloadStringTaskAsync( «https://ooo.com» );
>

По завершении операции в объекте Task сохраняется информация о том, завершилась ли она успешно или с ошибкой. Получить к ней доступ проще всего с помощью свойства IsFaulted, которое равно true, если во время выполнения операции произошло исключение.
Оператор await знает об этом и повторно возбуждает исключение, хранящееся в Task.
У читателя, знакомого с системой исключений в .NET, может возникнуть вопрос, корректно ли сохраняется первоначальная трассировка стека исключения при его повторном возбуждении.

Раньше это было невозможно; каждое исключение могло быть возбуждено только один раз. Однако в .NET 4.5 это ограничение снято благодаря новому классу ExceptionDispatchInfo, который взаимодействует с классом Exception с целью запоминания трассировки стека и воспроизведения ее при повторном возбуждении.

Async-методы также знают об исключениях. Любое исключение, возбужденное, но не перехваченное в async-методе, помещается в объект Task, возвращаемый вызывающей программе. Если в этот момент вызывающая программа уже ждет объекта Task, то исключение будет возбуждено в точке ожидания. Таким образом, исключение передается вызывающей программе вместе со сформированной виртуальной трассировкой стека – точно так же, как в синхронном коде.

Я называю это виртуальной трассировкой стека, потому что стек – вообще-то принадлежность потока, а в асинхронной программе реальный стек текущего потока может не иметь ничего общего с трассировкой стека в момент исключения. В исключении запоминается трассировка стека, отражающая намерение программиста, в ней представлены те методы, который программист вызывал сам, а не детали того, как C# исполнял части этих методов в действительности.

Асинхронные методы до поры исполняются синхронно Выше я уже отмечал, что async-метод становится асинхронным , только встретив вызов асинхронного метода внутри оператора await . До этого момента он работает в том потоке, в котором вызван, как обычный синхронный метод.

Иногда длительная операция не отправляет запросов в сеть и не обращается к диску, а просто выполняет продолжительное вычисление.
Разумеется, нельзя рассчитывать на то, что поток быстро выполнит, желательно избежать зависания пользовательского интерфейса.
Для этого мы должны вернуть управление потоку пользовательского интерфейса, чтобы он мог обрабатывать другие события, а длительное вычисление производить в отдельном потоке.

Класс Task позволяет это сделать, а для обновления пользовательского интерфейса по завершении вычисления мы можем, как обычно, использовать await:

Метод Task.Run исполняет переданный ему делегат в потоке, взятом из пула ThreadPool. В данном случае я воспользовался лямбда-выражением, чтобы упростить передачу счетной задаче локальных переменных. Возвращенная задача Task запускается немедленно, и мы можем дождаться ее завершения, как любой другой задачи:

Это очень простой способ выполнить некоторую работу в фоновом потоке.
Если необходим более точный контроль над тем, какой поток производит вычисления или как он планируется, в классе Task имеется статическое свойство Factory типа TaskFactory. У него есть метод StartNew с различными перегруженными вариантами для управления вычислением:

В разделе «Task и await» выше мы видели, как просто организовать выполнение несколько параллельных асинхронных задач, – нужно запустить их по очереди, а затем ждать завершения каждой. Потом мы узнаем, что необходимо дождаться завершения каждой задачи,
иначе можно пропустить исключения.

Для решения этой задачи можно воспользоваться методом Task.
WhenAll , который принимает несколько объектов Task и порождает агрегированную задачу, которая завершается, когда завершены все исходные задачи. Вот простейший вариант метода WhenAll (имеется также перегруженный вариант для коллекции универсальных объектов Task ):

Task WhenAll (IEnumerable tasks)

Основное различие между использованием WhenAll и самостоятельным ожиданием нескольких задач, заключается в том, что WhenAll корректно работает даже в случае исключений. Поэтому старайтесь всегда пользоваться методом WhenAll .

Универсальный вариант WhenAll возвращает массив, содержащий
результаты отдельных поданных на вход задач Task. Это сделано скорее для удобства, чем по необходимости, потому что доступ к исходным объектам Task сохраняется, и ничто не мешает опросить их свойство Result , так как точно известно, что все задачи уже завершены.

Часто возникает также ситуация, когда требуется дождаться завершения первой задачи из нескольких запущенных. Например, так бывает, когда вы запрашиваете один и тот же ресурс из нескольких источников и готовы удовлетвориться первым полученным ответом.

Task > WhenAny (IEnumerable > tasks)

В ситуации, когда возможны исключения, пользоваться методом WhenAny следует с осторожностью. Если вы хотите знать обо всех исключениях, произошедших в программе,
то необходимо ждать каждую задачу, иначе некоторые исключения могут быть потеряны. Воспользоваться методом WhenAny и просто забыть об остальных задачах – всё равно, что перехватить все исключения и игнорировать их.
Это достойное порицания решение, которое может впоследствии привести к тонким ошибкам и недопустимым состояниям программы.

Метод WhenAny возвращает значение типа Task >. Это означает, что по завершении задачи вы получаете объект типа Task .
Он представляет первую из завершившихся задач и поэтому гарантированно находится в состоянии «завершен». Но почему нам возвращают объект Task, а не просто значение типа T?

Чтобы мы знали, какая задача завершилась первой, и могли отменить все остальные и дождаться их завершения.

Task > anyTask = Task.WhenAny(tasks);
Task winner = await anyTask;
Image image = await winner; // Этот оператор всегда завершается синхронно

AddAFavicon(image);
foreach (Task eachTask in tasks)
<
if (eachTask != winner)
<
await eachTask;
>
>

Нет ничего предосудительного в том, чтобы обновить пользовательский интерфейс, как только будет получен результат первой завершившейся задачи (winner), но после этого необходимо дождаться остальных задач, как сделано в коде выше. При удачном стечении обстоятельств все они завершатся успешно, и на выполнении программы это никак не отразится. Если же какая-то задача завершится с ошибкой, то вы сможете узнать причину и исправить ошибку.

Отмена асинхронных операций связывается с типом CancellationToken

По соглашению всякий метод, поддерживающий отмену, должен иметь перегруженный вариант, в котором за обычными параметрами следует параметр типа CancellationToken

При вызове метода ThrowIfCancellationRequested отмененного объекта CancellationToken возбуждается исключение типа OperationCanceledException.

Библиотека Task Parallel Library знает, что такое исключение представляет отмену, а не ошибку, и обрабатывает его соответственно. Например, в классе Task имеется свойство IsCanceled , которое автоматически принимает значение true, если при выполнении async-метода произошло исключение OperationCanceledException.

Удобной особенностью подхода к отмене, основанного на маркерах CancellationToken , является тот факт, что один и тот же маркер можно распространить на столько частей асинхронной операции, сколько необходимо, – достаточно просто передать его всем частям.
Неважно, работают они параллельно или последовательно, идет ли речь о медленном вычислении или удаленной операции, – один маркер отменяет всё.

До первого await не происходит ничего интересного.
Async не планирует выполнение метода в фоновом потоке. Единственный способ сделать это – воспользоваться методом Task.Run , который специально предназначен для этой цели, или чем-то подобным.

В приложении с пользовательским интерфейсом это означает, что код до первого await работает в потоке пользовательского интерфейса.
А в веб-приложении на базе ASP.NET – в рабочем потоке ASP.NET.

Часто бывает, что выражение в строке, содержащей первый await, содержит еще один async-метод .
Поскольку это выражение предшествует первому await, оно также выполняется в вызывающем потоке.
Таким образом, вызывающий поток продолжает «углубляться» в код приложения, пока не встретит метод, действительно возвращающий объект Task .

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

К счастью, в большинстве случаев он выполняется недолго, но важно помнить, что одно лишь наличие ключевого слова async не гарантирует отзывчивости интерфейса.

Текущий контекст SynchronizationContext – это свойство текущего потока. Идея в том, что всякий раз, как код исполняется в каком-то специальном потоке, мы можем получить текущий контекст синхронизации и сохранить его.
Впоследствии этот контекст можно использовать для того, чтобы продолжить исполнение кода в том потоке, в котором оно было начато.

Поэтому нам не нужно точно знать, в каком потоке началось исполнение, достаточно иметь соответствующий объект SynchronizationContext .

В классе SynchronizationContext есть важный метод Post , который гарантирует, что переданный делегат будет исполняться в правильном контексте.

Мы знаем, что код, предшествующий первому await , исполняется в вызывающем потоке, но что происходит, когда исполнение вашего метода возобновляется после await ?

На самом деле, в большинстве случаев он также исполняется в вызывающем потоке, несмотря на то, что в промежутке вызывающий поток мог делать что-то еще.
Это существенно упрощает написание кода.

Для достижения такого эффекта используется класс SynchronizationContext .

Важно.
В момент приостановки метода при встрече оператора await текущий контекст SynchronizationContext сохраняется.
Далее, когда метод возобновляется, компилятор вставляет вызов Post , чтобы исполнение возобновилось в запомненном контексте.

В классе Task имеется свойство Result , обращение к которому блокирует вызывающий поток до завершения задачи.

Его можно использовать в тех же местах, что await , но при этом не требуется, чтобы метод был помечен ключевым словом async или возвращал объект Task .
И в этом случае один поток занимается – на этот раз вызывающий (то есть тот, что блокируется).

Большинство написанных вами async-методов возвращают значение типа Task или Task . Цепочка таких методов, в которой каждый ожидает завершения следующего, представляет собой асинхронный аналог стека вызовов в синхронном коде.

Компилятор C# прилагает максимум усилий к тому, чтобы исключения, возбуждаемые в этих методах, вели себя так же, как в синхронном случае. В частности, блок try-catch , окружающий ожидаемый async-метод, перехватывает исключения, возникшие внутри этого метода

async Task Catcher()
<
try
<
await Thrower();
>
catch (AlexsException)
<
// Исключение будет обработано здесь
>
>

async Task Thrower()
<
await Task.Delay(100);
throw new AlexsException();
>

Для этого C# перехватывает все исключения, возникшие в вашем async-методе . Перехваченное исключение помещается в объект Task , который был возвращен вызывающей программе. Объект Task переходит в состояние Faulted. Если задача завершилась с ошибкой, то ожидающий ее метод не возобновится как обычно, а получит исключение, возбужденное в коде внутри await .

В async-методе оно возбуждается там, где находится оператор await , а не в точке фактического вызова метода. Это становится очевидным, если разделить вызов и await .

Инструкция для тех, кто хочет стать программистом с нуля

Кризис заставляет многих сменить место работы или даже полностью поменять специальность. Приходится осваивать новые навыки и профессии. Прекрасным выбором в этом случае может стать профессия программиста. Это интересно, комфортно и прибыльно. В этой статье мы постараемся ответить на вопрос, как построить карьеру программиста с нуля.

Начать свой путь к карьере программиста стоит с ответа на вопрос, нужно ли вам программирование вообще? Этот вопрос не относится к тем, кто учится или учился по специальности, близкой к программированию. Если вам в школе математика давалась лучше, чем гуманитарные науки, если вам нравится проводить много времени за компьютером, если вам хочется изучать что-то новое, тогда программирование вам подойдёт.

С чего начать

Есть несколько вариантов развития событий, в результате которых человек становится программистом. Первый — родители-программисты, которые всему научили своих детей. Таким детям даже не нужно идти в университет. Второй вариант — модная профессия программиста. После школы нужно было выбрать, куда пойти учиться, и выбрали модное направление IT, вроде бы понравилось. И последний вариант — хобби, которое переросло в работу.

Если с вами ничего из вышеперечисленного не произошло, значит, у вас есть выбор из четырёх вариантов:

  • Самообразование. Этот вариант можно использовать как самостоятельно, так и в паре с другими методами. В интернете полно сайтов, книг и приложений, которые помогают изучать различные языки программирования и технологии. Но это самый тяжёлый путь для начинающих.
  • Университет. Если вы оканчиваете школу и хотите быть программистом, тогда идите в университет. Если не за знаниями, тогда за корочкой. Она может послужить бонусом при устройстве на работу. Хотя и какие-то знания вы тоже получите. Но не забывайте заниматься и самообучением. К выбору вуза стоит подойти очень ответственно. Внимательно изучите программы обучения и выбирайте лучшие технические вузы.
  • Ментор. Будет очень неплохо, если вы найдёте человека, который согласится помочь вам и направит вас в правильную сторону. Он подскажет подходящие книги и ресурсы, проверит ваш код, даст полезные советы. Кстати, мы уже писали о полезном ресурсе, где вы сможете найти ментора. Наставника можно искать среди знакомых программистов, на IT-тусовках и конференциях, на онлайн-форумах и так далее.
  • Специализированные практические курсы. Попробуйте поискать в своём городе курсы, где вас обучат какому-нибудь языку программирования или технологии. Я был приятно удивлён количеством таких курсов в Киеве, в том числе бесплатных и с последующим трудоустройством.

Какой язык, технологию и направление выбрать

Когда вы станете программистом, через годик-другой будете вольны выбирать любой язык, который вам нравится. Но при выборе первого языка программирования новичок должен учитывать следующие критерии:

  • Наличие на рынке вакансий. Конечная цель этого пути — найти работу программистом. А это будет трудно сделать, если на рынке вакансий никто не будет искать разработчиков на вашем языке программирования. Проверьте сайты с вакансиями, посмотрите, кого больше ищут, выпишите десяток языков. И переходите к следующему критерию.
  • Низкий уровень вхождения. Если вам придётся потратить длительное время на изучение языка, это может отбить у вас охоту к программированию вообще. Почитайте о тех языках, которые вы выбрали выше. Просмотрите литературу, которую нужно будет прочитать, чтобы изучить эти языки. И выберите те, о которых пишут, что они лёгкие, или которые вам показались лёгкими. Такими языками могут оказаться PHP, Ruby, Python.
  • Кайф от процесса. Если вам не нравится писать код на выбранном языке, вы не будете получать удовольствия от этого процесса, от работы и от жизни. А оно вам надо? Делайте правильный выбор.

Также вам придётся определиться с направлением программирования. Мобильное, десктопное, игры, веб, низкоуровневое программирование и так далее. Самые популярные и относительно лёгкие отрасли — разработка под веб, мобильные и десктопные клиенты. Под каждое направление может подходить один язык и совсем не подходить другой. То есть при выборе языка программирования также стоит отталкиваться и от этого фактора.

В любом случае изучите веб-технологии. Это язык разметки HTML, стили CSS и JavaScript, который позволит сделать вашу страницу динамической. На следующем этапе изучите серверный язык (Python, PHP, Ruby и другие) и подходящие для него веб-фреймворки. Изучите базы данных: практически в каждой вакансии программиста это упоминается.

Как получить начальный опыт

Без опыта вы не получите работу. Без работы вы не получите опыт. Замкнутый круг реальной жизни. Но ничего страшного, мы из него выберемся.

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

Во-вторых, вам нужно найти свои первые проекты. Это, наверное, самый сложный вариант, но рабочий. Вам придётся самому искать заказы, выполнять их, заморачиваться с оплатой. Для новичка это архисложно, но зато затем все остальные варианты покажутся плёвым делом. Выполненные проекты можно будет записать в опыт и показать вашему будущему работодателю. Реальные проекты являются большим плюсом в вашем резюме.

Если вы знаете английский язык, регистрируйтесь лучше на англоязычных биржах. Рынок там больше. Если не знаете английского, учите его. А пока что вам доступны русскоязычные биржи фриланса. Ищите небольшие проекты, которые соответствуют вашему уровню знаний или чуть выше него. Подайте заявку на пару десятков таких заданий. И приготовьтесь получить море отказов. Но если одна-две заявки выстрелят, у вас будет шанс получить реальный опыт.

Ещё одним неплохим вариантом для получения реального опыта является open source. Таким проектам всегда нужны новые люди, пусть даже и новички. Вы можете поискать в проекте баги или посмотреть в баг-трекере и предложить методы их решения. Найти такие проекты легко на GitHub или других сервисах для хостинга кода. Не стесняйтесь задавать там вопросы.

Четвёртый вариант получения опыта — помощь знакомым программистам. Попросите их передать вам маленькие и несложные задания. Если что-то не будет получаться, у вас всегда будет к кому обратиться. И при этом вы будете участвовать в реальном проекте.

Последний способ — собственные проекты, различные хакатоны или работа в коворкинге. Свои проекты сложно начинать самому, лучше поискать знакомых или друзей.

Почему стоит выбрать Python

Давайте немного подробнее поговорим о выборе первого языка программирования. Первый язык должен быть простым и популярным на рынке. Таким языком является Python. Я очень советую выбрать именно его в качестве первого языка программирования.

Код программы на Python читабелен. Вам даже не нужно быть программистом, чтобы в общих чертах понять, что происходит в программе. Из-за несложного синтаксиса Python вам понадобится меньше времени для написания программы, чем, например, на Java. Огромная база библиотек, которая сэкономит вам кучу сил, нервов и времени. Python является высокоуровневым языком. А значит, вам не нужно особо думать о ячейках памяти и о том, что там разместить. Python — язык широкого назначения. И он такой простой, что даже дети могут его выучить.

Справедливости ради стоит упомянуть и о других языках программирования. Java может стать неплохим выбором для новичка. Этот язык популярнее, чем Python, но и немного сложнее. Зато инструменты для разработки гораздо лучше проработаны. Стоит только сравнить Eclipse и IDLE. После Java вам будет проще перейти к работе с низкоуровневыми языками программирования.

PHP — ещё один очень популярный язык. И, мне кажется, он даже проще, чем Python. Очень легко найти себе ментора или решение какой-нибудь проблемы на форуме. Всё потому, что в мире существует огромное количество PHP-программистов разного уровня. В PHP нет нормального импорта, есть множество вариантов решения одной и той же задачи. А это усложняет обучение. И PHP заточен исключительно под веб.

Языки C и C# очень сложны для новичка. Ruby — хороший выбор в качестве второго языка, но не первого. JavaScript — очень простой язык, но ничему хорошему он вас не научит. А задача первого языка программирования всё-таки научить вас чему-то правильному, задать какую-то логику.


Важен ли английский язык

Важен! Не знаете? Учите. Знаете? Совершенствуйте. Учитесь читать, писать, слушать и говорить на английском. Делайте упор на техническую литературу. Слушайте англоязычные подкасты. Читайте англоязычные учебники по программированию.

Что нужно знать, кроме языка программирования

Конечно же, кроме языка программирования и английского, нужно знать что-то ещё. А вот что — зависит от направления, которое вы выберете. Веб-программист обязан знать HTML, CSS, JavaScript. Десктоп-программист учит API операционной системы и различные фреймворки. Разработчик мобильных приложений учит фреймворки Android, iOS или Windows Phone.

Всем нужно выучить алгоритмы. Попробуйте пройти курс на Coursera или найти подходящую для себя книгу по алгоритмам. Кроме этого, нужно знать одну из баз данных, паттерны программирования, структуры данных. Стоит также познакомиться с репозиториями кода. Хотя бы с одним. Обязательно знание систем версионного контроля. Выбирайте Git, он самый популярный. Вам нужно знать инструменты, с которыми вы работаете, операционную систему и среду разработки. И главный навык программиста — уметь гуглить. Без этого вы не проживёте.

Последние шаги

Вам нужно подготовить резюме. Не просто резюме, а хорошее резюме. Не стоит писать там лишней информации, но и умалчивать о своих умениях тоже не нужно. После того как вас пригласят на интервью, вы должны к нему подготовиться. Пройдитесь по материалу, который указан в вашем резюме. Вы должны быть уверены в своих знаниях. Просмотрите проекты, над которыми вы работали, вспомните технологии, которые вы применяли. И вперёд — к светлому будущему с новой профессией программиста.

Асинхронное программирование

В предлагаемой статье пойдет разговор о написании Concurrent кода. Зачастую программисты, не имеющие достаточной теоретической базы, не в состоянии аргументировать принимаемые ими при разработке приложений решения. Их знания строятся на некоторых предположениях, домыслах и, естественно, на шишках, которые им уже пришлось набить. Однако все перечисленное не гарантирует понимания и фундаментальных знаний. Обычно, найдя некоторый обходной путь, программист превозносит это решение как единственно правильное и безоговорочно верное. Никто не идеален, и каждый может легко признать нехватку знаний в различных областях, тем не менее, обмен опытом можно и нужно произвести.

Мастер Йода рекомендует:  PHP. Фильтрация данных PHP

Многие слышали об АРМ (Asynchronous Programming Model) – одном из известных паттернов для реализации выполнения асинхронного кода на платформе .NET. Существует и альтернативная модель асинхронного выполнения, которая построена на использовании событийной модели. События используются для уведомления вызывающего кода. В самом простом случае это код, который выполняется в созданном программистом дополнительном потоке и по окончании выполнения уведомляет родительский поток об окончании своей работы, вследствие чего родительский поток имеет возможность воспользоваться результатами труда дочернего.

Правильного ответа на вопрос «какую именно модель стоит использовать при разработке приложений» не существует. Каждая модель имеет свои преимущества и недостатки. Впрочем, есть некое обобщенное правило. Если клиентом кода является библиотека, то обычно используется APM, если же графический интерфейс, то правильным выбором будет реализация асинхронных вызовов, основанных на событийной модели.

Типичным представителем рассматриваемой модели является общеизвестный BackgroundWorker класс, сэкономивший программистам большое количество времени, но обсуждать этот класс неинтересно, так как он хорошо известен любому читателю, заинтересованному в обсуждаемой теме. Вместо этого в статье рассказывается, как правильно писать код, реализуя классы, подобные классу BackgroundWorker. Проблемы синхронизации cross-thread и GUI операций обсуждаются вскользь, основное время посвящено написанию реально полезного кода, который продемонстрирует на практике использование описываемого паттерна и поможет лучше понять, что может предложить.NET Framework и как нужно стараться писать свой код.

Для начала рассмотрим типичную задачу: инициализацию формы данными, получение которых требует значительного времени.

Довольно часто встречается ситуация «подвисания» некоторых приложений и форм в момент выполнения запрашиваемых пользователем действий. Чтобы понять, почему так происходит, достаточно понимать, что GUI в Windows всегда выполняется в единственном ассоциированном с ним потоке. Каждая презентационная технология в рамках .NET реализует специальный внутренний менеджер, который неустанно проверяет и пресекает все попытки работать с GUI из других потоков. Можно иметь много потоков, но взаимодействовать с GUI можно только в одном единственном. Бесполезно рассуждать, почему это именно так и кто виноват, это данность, с которой следует смириться. Можно только заметить, что это не всегда плохо, и такая модель предлагает некоторые бонусы. Так вот, как только приложение выполняет код, который занимает какое-либо значимое время из потока GUI, GUI перестает обрабатывать все поступающие ему сообщения, выполнение потока приостанавливается до конца выполнения кода, что приводит к отсутствию перерисовки, «подвисанию» и появлению различных сопутствующих явлений.

Далеко за примером ходить не надо: каждый хоть раз пользовался мастером Data Connection – Add Connection в студийном Server Explorer. После попытки выбрать сервер из комбобокса, окно уходит в «даун», а если попытаться подергать хидер, появляется устрашающая надпись о том, что приложение не отвечает на запросы. Конечно, это не так, рано или поздно нужный код выполнится, можно будет выбрать нужный сервер из списка. Причина происходящего очевидна. В момент опроса сети на наличие экземпляров было «заморожено» выполнение потока GUI, и приложение в это время не было способно сделать, что-либо полезное. Это плохой способ проектирования. Гораздо лучше стараться держать поток GUI свободным. Идеальный GUI выполняет в GUI потоке исключительно обращения к GUI элементам (чтение, запись) и синхронизацию, весь остальной код выполняется в параллельных потоках. Мир не идеален, но можно постараться сделать так, что бы он хотя бы чуть-чуть приблизился к этому.

Чтобы решить вышеприведенную проблему, достаточно тот код, который опрашивает сеть, выполнить в независимом от GUI потоке, а потом сообщить основному потоку, что код выполнился, передать результат его выполнения кода в основной поток, произвести синхронизацию и обновить данные в контролах. Звучит устрашающе, на в предложенной статье подробно исследуется первая частью проблемы, а известная статья PIL поможет разобраться с синхронизацией и обновлением. Все действительно просто.

Синхронное API определяется следующим образом:

Легко понять, что должна делать реализация данного интерфейса. Она должна возвращать список объектов, каждый из которых опишет доступный сервер в сети. Ниже для полноты картины приводится реализация NetworkInstanceDescriptor:

.NET предлагает несколько способов выполнения перечисления серверов. В предлагаемой ниже имплементации используются возможности SMO. Для того, что бы код компилировался, нужно подключить в проект сборку Microsoft.SqlServer.Smo и добавить соответствующее пространство имен:

В реализации синхронного API нет ничего из области Rocket Science, после выполнения запроса полученный DataTable обрабатывается построчно, а на выходе генерируется нужная коллекция.
Программисты, реализовывавшие вышеописанный визард, об этом не подумали. Они просто вызвали синхронный метод, установили курсор в часики, после получения результата, заполнили комбобокс, сбросили курсор в дефолт и двинулись дальше. Так написано 90% всего программного обеспечения.

Для добавления асинхронного метода потребуется, прежде всего, сам метод, который приведет к асинхронному вызову, а также событие, подписавшись на которое, клиент класса получит уведомление о том, что код выполнен, и сможет через аргументы события получить результат его выполнения. Метод void EnumAvailableSqlServersAsync() ничего прямо не возвращает клиенту потому тип возвращаемого значения void. Синхронный метод не требовал аргументов, потому и в асинхронном варианте они не требуются. Окончание Async в названии метода – правило хорошего тона. Программист, который будет пользоваться API, сможет сразу определить, что вызов метода приводит к асинхронной операции, а следовательно, вполне возможно, ему потребуется подписаться на событие, чтобы получить результат работы метода. Отметим, что в этом методе будет создаваться дополнительный поток, в котором и будет вызываться синхронная реализация.
.NET Framework предоставляет стандартный базовый класс аргумента, который должен использоваться в данном случае, это AsyncCompletedEventArgs. В нем определены два важных свойства: Canceled и Error. Первое позволяет обработчику узнать, что выполнение потока было прервано по желанию пользователя, второе предоставляет возможность коду, выполняемому в дочернем потоке, сообщить родительскому об исключении, произошедшем при его выполнении.
Это очень важно.
Проведем один небольшой эксперимент.

Исключение поймано, и программа продолжает работать. Можно корректно обработать пойманное исключение, а затем восстановить нормальную работу приложения (если ошибка не была фатальной) или завершить приложение в мягкой для него форме.
Пусть ThrowExceptionMethod вызывается из другого потока:

Catch не возымел никакого действия. Необработанное в порожденных потоках исключение приводит к фатальному сбою приложения. В .NET, как видно из примера, существуют механизмы, которые мешают в главном потоке обработать исключения дочернего. Единственное, что остается – подписаться на AppDomain.UnhandledException, но это может оказаться полезным разве что для логирования. Восстановить работу приложения все равно не удастся. Следовательно, обработка исключений в дочернем потоке является обязательной, если приложение не собирается аварийно завершаться из-за ошибки.
Ниже приводится пример обработки исключения в дочернем потоке.

Приложение продолжает работать. Следует, впрочем, помнить, что просто ловить исключения нехорошо, их всегда нужно обрабатывать или бросать дальше, если они произошли в коде библиотеки.
Возвращаясь к AsyncCompletedEventArgs и к его свойству Error, можно сказать, что оно предоставляет удобный способ обработать исключение дочерним потоком и выслать его «посылкой» главному. Если нужно передавать главному потоку не только обработанное исключение, но и результат выполнения метода, этот класс следует унаследовать. Хорошо, если наследник будет более универсальным и сможет использоваться с различными типами возвращаемых значений при написании любого ассинхронного API, хотя решение и без Generic имеет смысл во многих случаях разработки специализированных библиотеки.

Расширить наш интерфейс теперь выглядит так:

Был добавлен метод и событие, подписавшись на которое, клиент кода сможет корректно обработать полученный результат. Для генерации события воспользуемся стандартным паттерном:

Метод, запускающий код в отдельном потоке, выглядит следующим образом:

Вместо создания потока вручную, был использован ThreadPool. Вместо явной реализации метода, удовлетворяющего WaitCallback, было использовано лямбда выражение, вследствие чего код стал не только короче, но и понятнее. Реализация через ThreadPool прямолинейна, но имеет ряд ограничений. Впрочем, обычно этого достаточно для приложения. Главное, что это просто и безопасно, а изменить реализацию на более сложную всегда можно при надобности. Отдельно стоит отметить, что реализация возможности множественных вызовов асинхронного метода здесь никак не обрабатывается, хотя было бы не плохо это предусмотреть.
Можно было реализовать такую возможность через управляющий флаг (IsBusy или InProgress), что позволило бы клиенту класса проверять возможность повторного обращения, а код, обнаруживая множественные вызовы, мог бы бросать исключение. Другой способ менее тривиален и состоит в том, что бы передавать в Async метод дополнительный параметр, уникально идентифицирующий вызов (object tasked). При генерации события для клиента класса этот taskId мог бы передаваться обработчику. Дополнительный плюс состоит в том, что данный идентификатор можно использовать при реализации возможности отмены конкретных «тасков».
Стоит обратить внимание, что исключения, которые могут возникнуть при выполнении кода в порождаемом пулом потоке, перехватываются. В случае возникновения такого исключения генерируется событие, и в конструктор аргумента передается само исключение, если же код выполнился нормально, генерируется событие, которое позволит вызывающему коду получить результат выполнения.
Ниже приводится консольное приложение, которое использует реализованный класс.

Здесь происходит подписка на событие и вызов асинхронный метода. Обработчик получает результат и выводит его на консоль.

Используя такую модель при работе с GUI, нельзя забывать синхронизировать доступ. Можно синхронизировать код с помощью cross-thread в GUI. Зачастую этот подход обоснован, но он не единственен. Например, выше был упомянут компонент BackgroundWorker, который умеет работать с GUI и синхронизировать операции его обновления. Кроме того, можно ввести автоматический маршалинг кода, выполняемого асинхронно, в поток GUI. Это, безусловно, усложняет код, но делает работу с классом более удобной. .NET Framework позволяет использовать удобные механизмы для маршалинга кода с потока в поток.
Базовую функциональность, связанную с маршалингом, предоставляет класс SynchronizationContext. Его наследники обычно реализуют возможность синхронизации вызовов для конкретной технологии. Такие классы имеются в WPF, WindowsForms, APS.NET. Наследование используется для обеспечения возможности работать с контекстом через базовый класс, не задумываясь при написании компонентов о конкретной технологии. Самыми важными методами контекста синхронизации являются Post и Send. Именно они производят маршализацию. Базовый класс имеет наивные реализации. Например, Post использует ThreadPool, а Send – обычный синхронный вызов делегата, зато эти методы объявлены как виртуальные, что и позволяет классам наследникам определять свои собственные механизмы маршалинга. Ниже показана реализация методов в классе SynchronizationContext:

Для вызова метода через делегат используется пул потоков.

Идет прямой вызов посредством делегата.

Ниже показана реализация методов в классе WindowsFormsSynchronizationContext, наследнике контекста синхронизации.

Метод Post пользуется тем, что контекст имеет ссылку на Control из Windows Forms, а каждый контрол предоставляет метод BeginInvoke, позволяющий запустить код в потоке GUI. В методе Send ситуация не сложнее: когда контекст создается, он получает ссылку на поток this.DestinationThread = Thread.CurrentThread; это код из конструктора. После проверки валидности потока, применяется тот же прием что и в методе Post, но в синхронном исполнении. Ниже представлен полный код конструктора, понятный без дополнительных пояснений:

Статическое свойство Current возвращает ссылку на контекст.

Существует вспомогательный класс AsyncOperatonManager. Он имеет свойство SynchronizatoinContext, возвращающее ссылку на текущий контекст синхронизации, а также фабричный метод CreateCommand, который возвращает объект AsyncOperation. Свойство SynchronizatoinContext, проверяет, существует ли установленный контекст синхронизации, и если существует, возращает его, если же нет, возвращает дефолтную реализация SynchronizatoinContext. Это свойство просто экономит несколько лишних строчек кода при обращении к контексту. Например, не нужно проверять его на null. Метод CreateCommand создает команду, которая имеет ссылку на текущий контекст синхронизации. Она позволяет сохранить некое пользовательское состояние, кроме того, она имеет и собственное состояние (выполнена или нет). Когда команда выполнена, она может сообщить об этом контексту, что бы он корректно освободил необходимые ресурсы. Алгоритм следующий: посредством вызова AsyncOperatonManager?CreateOpperation, создается команда; передается, если необходимо, пользовательский объект, служащий параметром асинхронного метода; затем AsyncOperatonManager устанавливает команде текущий контекст, устанавливает команде поле _complited в false и вызывает метод контекста OperationStarted. Дефолтный контекст имеет пустую реализацию, зато наследники, такие как WindowsFormsSynchronizationContext, могут определять собственную логику по отслеживанию операции маршалинга (хотя за исключением контекста для ASP.NET, никто этого не делает).
Каждая команда имеет методы Post и Send, которые делегируют всю нужную работу ассоциированному контексту, проверяя, что делегат не null, а собственное состояние команды установлено в «не выполнено». Команда в состоянии «выполнена» более не пригодна к использованию. Это сделано для того, чтобы корректно освобождать ресурсы. Еще один полезный метод – PostOperationComplited, он тоже делегирует вызов через Post, но дополнительно устанавливает команду в состояние «выполнена».

Ниже рассматривается применения на практике вышеизложенных теоретических сведений. Объявляется переменную private AsyncOperation _currentOperation;

Пусть клиентский код не допускает несколько параллельных асинхронных вызовов.
В EnumAvailableSqlServersAsync инициализируется команда. Вот тело метода:

Вместо непосредственной генерации события об окончании выполнения, вызывается CombineResult для сбора EnumAvailableSqlServersEventArgs > и выполнения маршализации в поток GUI с помощью команды, вызывая для ней PostOperationCompleted. Ниже приведена реализация этого метода:

Первым параметром метода PostOperationCompleted является делегат SendOrPostCallback. До вызова метода, назначенного делегату, мы находимся в коде, вызываемом через пул, после же этого (после маршализации), оказываемся в потоке GUI, если, конечно, код выполнялся в WinForms или WPF.

Делегат предварительно нужно объявить в программе:

и в конструкторе класса проинициализировать его:

Для инициализации используется следующий метод:

Внутри этого метода мы уже в потоке GUI, если код выполнялся в WinForms или WPF. То есть, уже имеется маршализированное значение, и остается только сгенерировать соответствующее событие для уведомления клиента о том, что операция выполнена.

Последний штрих – добавление свойства IsBusy.

Когда вызывается EnumAvailableSqlServersAsync, нужно проверить, что код не занят:

и установить IsBusy в true, а после выполнения маршализации можно снова разрешить использование метода EnumAvailableSqlServersAsync:

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

Ниже приводится код с небольшими комментариями:

Пишем программы на C++ с применением PPL

Эта статья написана по предварительным версиям Windows 8 и Visual Studio 2012. Любая изложенная здесь информация может быть изменена.

Продукты и технологии:

C++, Windows 8, Visual Studio 2012, Visual Studio Parallel Patterns Library

В статье рассматриваются:

  • асинхронное программирование в WinRT;
  • анатомия асинхронной операции WinRT;
  • композиция нескольких асинхронных операций;
  • использованием PPL-задач (Parallel Patterns Library);
  • обработка ошибок и отмена операций.

Говорят, ассистенты голливудских режиссеров, ведающие подбором актерского состава, частенько отделываются от претендентов пренебрежительными словами «не звоните нам — мы сами вас вызовем». Но для разработчиков эта фраза описывает то, как работают многие инфраструктуры: вместо того чтобы дать программисту возможность управлять потоком выполнения для всего приложения, инфраструктура контролирует среду и запускает обратные вызовы или обработчики событий, предоставляемые программистом.

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

В Windows 8 асинхронные операции используются повсеместно, и WinRT предлагает новую модель программирования для согласованной работы с такой асинхронностью.

На рис. 1 демонстрируется базовый шаблон работы с асинхронными операциями. В этом коде C++-функция считывает строку из файла.

Рис. 1. Чтение из файла

Первым делом обратите внимание на то, что тип значения, возвращаемого функцией ReadString, — void. Все правильно: эта функция не возвращает значение; вместо этого она принимает обратный вызов и запускает его, когда становится доступным результат. Добро пожаловать в мир асинхронного программирования: не звоните нам — мы сами вас вызовем!

Анатомия асинхронной операции WinRT

В основе асинхронности в WinRT лежат четыре интерфейса, определенные в пространстве имен Windows::Foundation: IAsyncOperation, IAsyncAction, IAsyncOperationWithProgress и IAsyncActionWithProgress. Все потенциально блокирующие или длительно выполняемые операции в WinRT определены как асинхронные. По соглашению, имя метода заканчивается на «Async», а возвращаемый тип соответствует одному из этих четырех интерфейсов. Таков метод GetFileAsync на рис. 1, который возвращает IAsyncOperation . Многие асинхронные операции не возвращают значения, и их тип — IAsyncAction. Операции, способные уведомлять о своем прогрессе, предоставляются через IAsyncOperationWithProgress и IAsyncActionWithProgress.

Чтобы указать обратный вызов завершения для асинхронной операции, задается свойство Completed. Это свойство является делегатом, который принимает асинхронный интерфейс и состояние завершения. Хотя этот делегат можно создать с помощью указателя на функцию, чаще всего вы будете пользоваться лямбдой (полагаю, что на сегодняшний день вы уже знакомы с этой частью C++11).

Очень часто вы будете обнаруживать, что используете несколько асинхронных операций совместно.

Чтобы получить значение операции, вы вызываете метод GetResults интерфейса. Заметьте: хотя это тот же интерфейс, который возвращается из вызова GetFileAsync, в обработчике завершения можно вызвать только GetResults этого интерфейса.

Второй параметр делегата завершения — AsyncStatus, который возвращает состояние операции. В реальном приложении вы проверили бы его значение перед вызовом GetResults. На рис. 1 я опустил эту часть для краткости.

Очень часто вы будете обнаруживать, что используете несколько асинхронных операций совместно. В своем примере я сначала получаю экземпляр StorageFile (вызовом GetFileAsync), затем открываю его, используя OpenAsync и получая IInputStream. Далее я загружаю данные (LoadAsync) и читаю их с помощью DataReader. Наконец, я получаю строку и вызываю переданную пользователем функцию обратного вызова.

Композиция

Отделение начала операции от ее завершения весьма важно для исключения блокирующих вызовов. Проблема в том, что композиция нескольких асинхронных операций, основанных на обратных вызовах, — дело трудное и конечный код сложно читать и отлаживать. Нужно что-то упрощать, чтобы можно было разобраться в получившейся «каше вызовов».

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

Однако, если вы не видите дефект в этом решении, подумайте о следующем: когда вы будете начинать чтение из file2? Действительно ли вам нужно заканчивать чтение первого файла до начала чтения второго? Конечно, нет! Куда лучше запускать несколько асинхронных операций и обрабатывать данные по мере их поступления.

Давайте попробуем. Для начала — из-за запуска двух операций одновременно и возврата из функции до завершения операций — мне потребуется специальный объект, создаваемый в куче, для хранения промежуточных результатов. Назовем его ResultHolder:

Как показано на рис. 2, операция, успешно завершившаяся первой, установит его член results->str. Операция, завершенная второй, будет использовать его для формирования конечного результата.

Рис. 2. Параллельное чтение из двух файлов

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

Задачи в Parallel Patterns Library

Visual Studio Parallel Patterns Library (PPL) предназначена для упрощения и ускорения написания параллельных и асинхронных программ на C++. Вместо того чтобы работать на уровне потоков и их пулов, пользователи PPL имеют дело с более высокоуровневыми абстракциями, такими как задачи (tasks), параллельными алгоритмами вроде parallel_for и parallel_sort, и контейнерами с поддержкой параллельной обработки, например concurrent_vector.

PPL-класс task (новшество в Visual Studio 2012) позволяет вам кратко описать индивидуальную единицу работы, которую нужно выполнять асинхронно. Логику вашей программы можно выразить в терминах независимых (или взаимозависимых) задач и предоставить исполняющей среде позаботиться об оптимальном планировании этих задач.

Самое полезное в задачах — возможность их композиции. В простейшем случае две задачи можно последовательно соединить, объявив одну из них продолжением (continuation) другой. Эта обманчиво тривиальная конструкция позволяет комбинировать несколько задач весьма интересными способами. Многие высокоуровневые PPL-конструкции вроде join и choice (о которых чуть позже) сами построены на основе этой концепции. Продолжения задач также можно использовать для более точного представления завершений асинхронных операций. Давайте пересмотрим пример с рис. 1 и теперь напишем его, используя PPL-задачи, как показано на рис. 3.

Рис. 3. Чтение из файлов с применением вложенных PPL-задач


Поскольку теперь для представления асинхронности я использую задачи вместо обратных вызовов, здесь нет обратного вызова, передаваемого пользователем. Эта инкарнация функции возвращает задачу.

В данной реализации я создал задачу getFileTask из асинхронной операции, возвращаемой GetFileAsync. Затем я определяю завершение этой операции как продолжение задачи (метод then).

Метод then заслуживает более внимательного рассмотрения. Параметр этого метода является лямбда-выражением. На самом деле он мог бы быть и указателем на функцию, и объектом-функцией, и экземпляром std::function, но, поскольку лямбда-выражения вездесущи в PPL (и в современном C++), с этого момента я буду говорить «лямбда», имея в виду любой тип вызываемого объекта.

Метод then возвращает задачу некоего типа T. Этот тип T определяется возвращаемым типом лямбды, переданной в then. В самом базовом виде, когда лямбда возвращает выражение типа T, метод then возвращает task . Например, лямбда в следующем продолжении возвращает int; следовательно, конечным типом будет task :

Гораздо лучше запускать несколько асинхронных операций сразу и обрабатывать данные по мере их поступления.

Тип продолжения, используемого на рис. 3, несколько отличается. Он возвращает задачу и выполняет асинхронную развертку (unwrapping) этой задачи, чтобы конечный тип был не task >, а task :

Если все это пока не совсем понятно, не зацикливайтесь на этом. Обещаю, что после еще нескольких примеров вы точно уловите смысл.

Композиция задач

Вооружившись тем, о чем было рассказано в предыдущем разделе, продолжим обсуждение, опираясь на все тот же пример с чтением файлов.

Вспомните, что в C++ все локальные переменные, находящиеся в функциях и лямбдах, теряются при возврате управления. Чтобы сохранить состояние, вы должны сами копировать переменные в кучу или другое длительно живущее хранилище. Именно по этой причине я ранее создал класс holder. В лямбдах, выполняемых асинхронно, нужно быть осторожным, чтобы не захватить какое-либо состояние из включающей функции по указателю или ссылке; иначе, когда эта функция завершится, вы останетесь с указателем на недействительный участок памяти.

Я воспользуюсь тем фактом, что метод then выполняет развертку в асинхронных интерфейсах, и перепишу наш пример в более четкой форме — несмотря на издержки введения еще одной структуры для хранения (рис. 4).

Рис. 4. Объединение нескольких задач в цепочку

В сравнении с примером на рис. 3 этот код легче читать, потому что он подобен последовательным операциям в противоположность «лестнице» вложенных операций.

Кроме метода then, в PPL есть несколько других композиционных конструкций. Одна из них — операция join, реализованная в методе when_all. Метод when_all принимает последовательность задач и возвращает конечную задачу, которая собирает вывод ото всех составляющих ее задач в std::vector. В случае двух аргументов (что встречается довольно часто) PPL предлагает удобное сокращение: оператор &&.

Вот как я использовал оператор join для пересмотренной реализации метода конкатенации файлов:

Операция choice тоже полезна. При наличии серии задач choice (которая реализована в методе when_any) завершается, когда заканчивается выполнение любой задачи в последовательности. Как и join, у choice есть сокращение для двух аргументов, но в виде оператора ||.

Choice удобна в таких сценариях, как избыточное или спекулятивное выполнение; вы запускаете несколько задач и получаете результат от той, которая завершится первой. Вы также могли бы добавить период ожидания операции — начать с операции, которая возвращает некую задачу, и скомбинировать ее с задачей, которая «засыпает» на заданное время. Если спящая задача завершается первой, период ожидания выполнения вашей операции истек, и ее можно отбросить или отменить.

В PPL имеется еще одна конструкция, помогающая в композиции задач: task_completion_event, которую можно использовать для взаимодействия задач с кодом, где не применяется PPL. Конструкцию task_completion_event можно передавать в поток или обратный вызов завершения ввода-вывода, где ожидается, что это событие в конечном счете будет установлено. Задача, созданная на основе task_completion_event, будет завершена, как только будет установлено task_completion_event.

Создание асинхронных операций с помощью PPL

Всякий раз, когда нужно выжать из компьютера все, на что он способен, в качестве языка программирования следует выбирать C++. Остальные языки занимают в Windows 8 свои ниши: комбинация JavaScript/HTML5 отлично подходит для написания GUI-интерфейсов, C# обеспечивает быструю разработку программ и т. д. Чтобы написать приложение в стиле Metro, используйте тот язык, который вы знаете и который позволяет решать ваши задачи. Тем более что при разработке одного приложения можно задействовать сразу несколько языков программирования.

Зачастую вы предпочтете писать внешний интерфейс приложения на каком-нибудь языке вроде JavaScript или C#, а внутренние компоненты — на C++ для максимальной производительности. Если операция, экспортируемая вашим C++-компонентом, интенсивно нагружает процессор или выполняет большой объем операций ввода-вывода, желательно определить ее как асинхронную.

Для реализации ранее упомянутых асинхронных интерфейсов WinRT (IAsyncOperation, IAsyncAction, IAsyncOperation­WithProgress и IAsyncActionWithProgress) в PPL определены метод create_async и класс progress_reporter; оба находятся в пространстве имен concurrency.

PPL предназначена для упрощения и ускорения написания параллельных и асинхронных программ на C++.

В простейшей форме create_async принимает лямбду или указатель на функцию, которая возвращает некое значение. Тип лямбды определяет тип интерфейса, возвращаемого из create_async.

При передаче лямбды без параметров, которая возвращает тип T, отличный от void, метод create_async возвращает реализацию IAsyncOperation . В случае лямбды, возвращающей void, конечным интерфейсом будет IAsyncAction.

Лямбда может принимать параметр типа progress_reporter

. Экземпляр этого типа используется для передачи отчетов о прогрессе типа P вызвавшему коду. Например, лямбда, принимающая progress_reporter , может сообщать процент выполненной работы как целочисленное значение. Возвращаемый тип лямбды в данном случае определяет конечный интерфейс — IAsyncOperationWithProgress или IAsyncAction

(рис. 5).

Рис. 5. Создание асинхронных операций в PPL

Чтобы предоставить асинхронную операцию другим WinRT-языкам, определите открытый ref-класс в своем C++-компоненте и предусмотрите функцию, которая возвращает один из четырех асинхронных интерфейсов. Конкретный пример гибридного приложения на C++ и JavaScript вы найдете в PPL Sample Pack (чтобы получить его, выполните поиск в сети по «Asynchrony with PPL»). Вот фрагмент, который предоставляет процедуру преобразования изображения как асинхронную операцию с отчетом о прогрессе:

Как показано на рис. 6, клиентская часть приложения реализована на JavaScript.

Рис. 6. Использование процедуры преобразования изображения в JavaScript

Обработка ошибок и отмена

Внимательные читатели, вероятно, заметили: в этой статье по асинхронности до сих пор напрочь игнорировалось все, что касается обработки ошибок и отмены. Больше этой темой мы пренебрегать не будем!

Рано или поздно процедуре чтения файла будет передана ссылка на файл, которого нет или который нельзя открыть по какой-то причине. Функции поиска по словарю встретится неизвестное ей слово. Преобразование изображения не даст быстрого результата и будет отменено пользователем. В этих ситуациях операция прекращается досрочно, до предполагавшегося момента завершения.

Для указания ошибок или других исключительных ситуаций в современном C++ используются исключения. Они прекрасно работают в рамках одного потока: при генерации исключения происходит раскрутка стека до тех пор, пока по стеку вызовов не будет найдет подходящий блок catch. Однако все это существенно запутывается, когда мы имеем дело с параллельной обработкой, — исключение, исходящее из одного потока, не так-то просто поймать в другом.

Подумайте, что происходит при этом с задачами и продолжениями: когда тело задачи генерирует исключение, его поток выполнения прерывается и он не может дать какое-либо значение. А если нет значения, которое можно было бы передать в продолжение, оно тоже не может работать. Даже для задач, возвращающих void, т. е. не передающих никаких значений, вам нужно возможность сообщить следующей задаче о том, успешно ли завершилась предыдущая.

Вот почему существует альтернативная форма продолжения: для задачи типа T, лямбда продолжения обработки ошибок принимает task . Чтобы получить значение, созданное предыдущей задачей, вы должны вызвать метод get параметра-задачи. Если предшествующая задача завершилась успешно, то же самое будет и с методом get. В ином случае get сгенерирует исключение.

Здесь хотелось бы обратить ваше внимание на важный момент. Для любой задачи в PPL, в том числе созданной из асинхронной операции, синтаксически корректно вызывать ее метод get. Однако, прежде чем результат станет доступен, get пришлось бы блокировать вызвавший поток, и, конечно, это противоречило бы нашей мантре быстроты и гибкости. Поэтому вызывать get из task обычно не рекомендуется, а в STA запрещено (исполняющая среда сгенерирует исключение «недопустимая операция»). Единственный момент, когда вы можете вызвать get, — при получении задачи как параметра продолжения. Пример показан на рис. 7.

Мастер Йода рекомендует:  15 советов по написанию самодокументируемого кода (на примере JavaScript)

Самое полезное в задачах — возможность их композиции.

Рис. 7. Продолжение, обрабатывающее ошибки

Какой язык программирования стоит выучить первым ?

Введение

«Новичку в сфере IT стоит знать одно простое правило программирования — это написание очень тонких инструкций для глупой, но послушной машины».

Среди массы различных языков программирования новичку в IT очень сложно выбрать направления для дальнейшего развития, потому что каждый язык занимает определенную ячейку. Наиболее популярные, языки в «современном программировании», это: Java, Python, Objective-C, PHP, C, C++, C#, JavaScript и Ruby. Форумы и специализированные сайты переполнены тематикой «Что выбрать?» и «Куда развиваться?». И мы нашли оптимальную схему выбора ветки развития юным программистам.

С чего начать изучение программирования?

Итак, с чего же стоит начать? В первую очередь ответьте предельно честно самому себе на главный вопрос: «Почему Вы хотите начать изучать программирование?». Ответов много, а предпосылок еще больше, но если провести анализ, то мотивов стать разработчиками несколько.

  • Деньги
    Часто, наиболее распространенной причиной изучения программирования служит мнение о том, что программисты много зарабатывают. Да, это действительно так. Зарплаты хороших программистов могут заставить позавидовать даже некоторых менеджеров высшего звена. Но это достигается годами упорного труда, само мотивации и углубления в современные информационные технологии. Кроме того, дабы получать по-настоящему достойную зарплату, стоит сразу нацеливается на работу в хорошей крупной компании. Или же иметь идею на миллион и открывать свой start-up.
  • Перспективы
    Казалось бы, относительно недавно стоило появится первом персональному компьютеру – и нате Вам! – программисты стали одними из наиболее востребованных работниками современного рынка. Действительно, наш век – это век информационных технологий, быстроразвивающийся и динамический. Сейчас спрос на специалистов IT-сферы растет как никогда, и вряд ли стоит ждать на него спад. Все больше и больше современных компаний готовы предложить «теплое местечко». Сколько их – Google, Microsoft, EPAM прочие.
  • Динамичность
    Каждому человеку присуща толика любознательности. Но есть люди, у которых страсть изучать что-то новое просто в крови! Что же, в таком случае программирование – это именно то, что вам нужно. На данный момент существуют десятки различных направлений: мобильная разработка, настольные приложения, веб-программирование, игры, сколько их… А уж сколько для этих направлений было разработано технологий и программ – не счесть. Работая в сфере IT вы можете быть уверенным, что никогда не дадите себе заскучать!
    Итак, раз мы разобрались с нашими мотивами и разложили все по «полочкам», приступим же непосредственно к выбору первого языка программирования!

Какой язык программирования выбрать?

Теперь, самый важный вопрос: так какой же язык программирования стоит выбрать новичку в сфере IT? Прежде всего, все зависит от того, что именно вы желаете изучать.

Вот график наиболее востребованных направлений области информационных технологий:

Как мы можем видеть, список внушительный. Но что из этого вас прельщает больше всего? Давайте попробуем разобраться.

  • Я бы хотел разрабатывать сайты
    С этим направлением не все так просто. Есть Front-End разработка сайтов – это все, что вы видите на экране. Создание веб-страниц, программ для них, стилей и много чего прочего. В таком случае вам стоит обратить свое внимание на JavaScript и HTML & CSS. А есть Back-End – разработка непосредственно программ для серверов – тех алгоритмов, которые, собственно говоря, и будут управлять страницами, сайтом и прочим. Здесь все несколько сложнее, так как сразу же появляется хороший выбор из Python, Java, C#, и PHP. Каждый из этих языков обладает как серией достоинств, так и набором откровенных недостатков. В качестве этакого старта советуем рассмотреть Python.
  • Настольные приложения для домашних ПК
    Тут бесспорными лидерами выступают такие популярные языки, как Java и C#. С одной стороны, обучение Java несколько проще и быстрее, чем C#, с другой, набор возможностей, которыми может похвалится C#, на порядок выше.
  • Работа с базами данных
    Ну, тут все однозначно: следует начинать с SQL! Администрирование, работа с реляционными базами данными и прочее, что так необходимо в современно IT-мире. Здесь можно рассмотреть вступление в язык запросов.
  • Игры-игры-игры!
    Геймерами не стают, ими рождаются. Наслаждаетесь современными продуктами игровой индустрии и сами бы хотели привнести что-то в этой увлекательный виртуальный мир? Тогда, определенно, вам стоит обратить свое внимание на С++.

Здесь можно посмотреть статистику языков программирования по популярности:

Статистика поиска соискателей по языкам и платформам:

Какой самый простой язык программирования?

Если начинать изучать, что начинать с простого, не так ли? Итак, вот небольшой даждест «для чайников» с чего, собственно, стоит начать обучение.

  • JavaScript
    Один из наиболее легких языков программирования для веб-разработки. Динамическое наполнение web HTML страниц, целый комплекс технологий семейства React.JS, Node.JS и прочих, обилие библиотек и обучающих материалов + плюс, огромное комьюнити. Отличный старт для новичка в области IT! Вот вступление в видео-курс по JavaScript.
  • Python
    Очень легкий и приятный в освоении интерпретируемый язык универсального назначения. Обширное количество довольных пользователей, огромное количество вакансий на мировом рынке, поддержка большинством сред разработки и наличие специализированных сред разработки. Ознакомится с данным языком можно здесь.
  • PHP
    PHP – или Hypertext Preprocessor – достаточно мощный и, в то же время, легкий в освоении язык программирования для разработки серверной части веб-сайта. Имеет богатую историю и может похвалится хорошей базой различных библиотек. Ознакомится с ним вы можете здесь.

Самый сложный язык программирования

Быть может, вы уже имели определенный опыт, связанный с IT, и желаете попробовать себя в более «продвинутой» песочнице? Или вы не из тех, кто хочет изучать что-то простое, а сразу брать «быка за рога»? Что же, на этот случай мы можем предложить следующее:

  • Java
    Отменный компилируемый язык программирования для решения целого спектра задач – от написания настольных приложений до создания серверных программ для веб-сайтов. Имеет мощную аудиторию поклонников, богатую историю, корни которой простираются в самые 90-стые и множество библиотек для практически любой задачи. Начать изучение этого языка можно здесь.
  • С#
    Язык программирования от компании Microsoft универсального назначения. Java показалась простой? Тогда C# для вас! Имеет практически идентичный с Java синтаксис, но отличается расширенном набором функций и производимых операций. WPF для оконных приложений, ASP.NET для разработки сайтов и прочие высококачественные фреймворки для всех типов задач. Что может быть лучше, чем инструмент на все случаи жизни? Приступить к работе с этим языком можно здесь.
  • C++
    Легенда программирования. Универсальный язык программирования, возможности которого воистину не имеют ограничений. Низкоуровневая работа с памятью, разработка систем рендеринга для игр, отличная производительность и неисчерпаемый набор библиотек… С++ — это наиболее быстрый и оптимизированный язык программирования из всех существующих. Но его изучение потребует особенной усидчивости и трудолюбия. Сумеете ли вы с ним совладать?

Языки программирования, какой самый оплачиваемый?

Один из самых востребованных и высокооплачиваемых на рынке языков программирования — это Java. Очень популярен на всех платформах, ОС и устройствах, благодаря своей кроссплатформенности. Используется в Gmail, Minecraft, большинстве Android приложений и в корпоративных приложениях.

  • С — это «лингва франка» среди всех языков программирования. Один из самых старых и самых широко используемых языков в мире. Отлично подходит для системного и аппаратного программирования. Он используется в ОС и оборудовании.
  • С# был создан на платформе Microsoft, но совсем недавно вышел на open source. С# — это популярный выбор предприятий для разработки разнообразных web-сайтов и Windows приложений, используя .NET framework. С# используют для создания web сайтов при помощи web фреймворка от Microsoft – ASP.NET. Своим синтаксисом и функционалом похож на Java. Используется в корпоративных и Windows приложениях.
  • Objective-C является основным языком, используемый Apple для Mac OS X и iOS. Его стоит изучать, если Вы собираетесь разрабатывать только под OS X и iOS. Стоит задуматься над изучением Swift, как о следующем языке. Objective-C используется в большинстве iOS приложений и в части Mac OS X.
  • C++ — это более сложная версия языка программирования С, с существенно расширенным набором возможностей. Широко используется при разработке игр, промышленных и высокопроизводительных приложений. Изучать С++ — все равно что изучать, как производить, собирать и водить машину. Этот язык не рекомендуется для самостоятельного изучения и требует наличие ментора. Он широко используется в ОС, оборудовании и браузерах.

На самом деле, абсолютно не важно, с чего именно Вы начнете Ваш путь в сфере IT. Нужно знать хотя бы несколько основных языков и технологий, чтобы познать все аспекты программирования. А самое главное — начать!

Асинхронное программирование — всё по этой теме для программистов

Контекст синхронизации (SynchronizationContext) — позволяет возобновить выполнение метода в конкретном потоке.
Текущий контекст SynchronizationContext – это свойство текущего потока. Идея в том, что всякий раз, как код исполняется в каком-то специальном потоке, мы
можем получить текущий контекст синхронизации и сохранить его.
Впоследствии этот контекст можно использовать для того, чтобы продолжить исполнение кода в том потоке, в котором оно было начато.

В классе SynchronizationContext есть важный метод Post , который гарантирует, что переданный делегат будет исполняться в правильном контексте.

Важно! В момент приостановки метода при встрече оператора await текущий контекст SynchronizationContext сохраняется.
Далее, когда метод возобновляется, компилятор вставляет вызов Post , чтобы исполнение возобновилось в запомненном контексте.

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

К числу таких длительных операций можно отнести:
• сетевые запросы;
• доступ к диску;
• продолжительные задержки.

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

Всякий раз, запуская новый поток или пользуясь классом ThreadPool, вы пишете асинхронную программу , потому что текущий поток может продолжать заниматься другими вещами.

Асинхронный код освобождает поток , из которого был запущен. И это очень хорошо по многим причинам.

Часто существует лишь один поток, способный выполнить определенную задачу (например, поток пользовательского интерфейса) и, если не освободить его быстро, то приложение перестанет реагировать на действия пользователя.

Но самым важным мне кажется тот факт, что асинхронное выполнение открывает возможность для параллельных вычислений .

В версии C# 5.0 Microsoft добавила механизм, предстающий в виде двух новых ключевых слов: async и await .

Механизм async встроен в компилятор и без поддержки с его стороны не мог бы быть реализован в библиотеке. Компилятор преобразовывает исходный код, то есть действует примерно по тому же принципу, что в случае лямбда-выражений и итераторов в предыдущих версиях C#.


У ASP.NET-приложений на веб-сервере нет ограничения на единственный поток , как в случае программ с пользовательским интерфейсом. И тем не менее асинхронное выполнение может оказаться весьма полезным, так как для таких приложений характерны длительные операции, особенно запросы к базе данных.

В зависимости от версии IIS может быть ограничено либо общее число потоков, обслуживающих веб-запросы, либо общее число одновременно обрабатываемых запросов. Если большая часть времени обработки запроса уходит на обращение к базе данных, то увеличение числа одновременно обрабатываемых запросов может повысить пропускную способность сервера

Помните: основная отличительная особенность асинхронного кода состоит в том, что поток, начавший длительную операцию, освобождается для других дел .
В случае ASP.NET этот поток берется из пула потоков, поэтому после запуска длительной операции он сразу же возвращается в пул и может затем использоваться для обработки других запросов. Таким образом, для обработки одного и того же количества запросов требуется меньше потоков.

Библиотека Task Parallel Library была включена в версию .NET Framework 4.0. Важнейшим в ней является класс Task , представляющий выполняемую операцию. Его универсальный вариант, Task , играет роль обещания вернуть значение (типа T), когда в будущем, по завершении операции, оно станет доступно.

Как мы увидим ниже, механизм async в C# 5.0 активно пользуется классом Task. Но и без async классом Task, а особенно его вариантом Task , можно воспользоваться при написании асинхронных программ. Для этого нужно запустить операцию, которая возвращает Task , а затем вызвать метод ContinueWith для регистрации обратного вызова.
Это называется асинхронность вручную потому что используем метод ContinueWith .

Заметка на будующее:
Класс Task умеет в частности обрабатывать исключения и работать с контекстами синхронизации SynchronizationContext .
Мы увидим, что это полезно, когда требуется выполнить обратный вызов в конкретном потоке (например, в потоке пользовательского интерфейса).

1-ый шаг это пометка метода ключевым словом async .
Оно включается в сигнатуру метода точно так же, как, например, слово static.

2-ой шаг мы должны дождаться завершения скачивания, воспользовавшись ключевым словом await .
С точки зрения синтаксиса C#, await – это унарный оператор, такой же как оператор ! или оператор приведения типа (type).
Он располагается слева от выражения и означает, что нужно дождаться завершения асинхронного выполнения этого выражения.

На заметку!
Метод, помеченный ключевым словом async, автоматически не становится асинхронным .
Async-методы лишь упрощают использование других асинхронных методов. Они начинают исполняться синхронно, и так происходит до тех пор, пока не встретится вызов асинхронного метода внутри оператора await .
В этот момент сам вызывающий метод становится асинхронным.
Если же оператор await не встретится, то метод так и будет выполняться синхронно до своего завершения.

Я говорил, что класс Task представляет выполняемую операцию, а его подкласс Task – операцию, которая в будущем вернет значение типа T. Можно считать, что Task – это обещание вернуть значение типа T по завершении длительной операции.
Оба класса Task и Task могут представлять асинхронные операции, и оба умеют вызывать ваш код по завершении операции. Чтобы воспользоваться этой возможностью вручную, необходимо вызвать метод ContinueWith , передав ему код, который должен быть выполнен, когда длительная операция завершится. Именно так и поступает оператор await , чтобы выполнить оставшуюся часть async-метода .

Если применить await к объекту типа Task , то мы получим выражение await , которое само имеет тип T .
Это означает, что результат оператора await можно присвоить переменной, которая используется далее в методе, что мы и видели в примерах.
Однако если await применяется к объекту неуниверсального класса Task , то получается предложение await , которое ничему нельзя присвоить (как и результат метода типа vo >Task не обещает вернуть значение в качестве результата, а представляет лишь саму операцию.

Метод, помеченный ключевым словом async , может возвращать значения трех типов:
• void
• Task
• Task , где T – некоторый тип.

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

Я провожу различие между типом возвращаемого методом значения (например, Task ) и типом результата, который нужен вызывающей программе (в данном случае string).
В обычных, не асинхронных, методах тип возвращаемого значения совпадает с типом результата, тогда как в асинхронных методах они различны – и это очень существенно.

Использовать тип void следует, когда вы уверены, что вызывающей программе безразлично, когда завершится операция и завершится ли она успешно.

Async-методы , возвращающие тип Task , позволяют вызывающей программе ждать завершения асинхронной операции и распространяют исключения, имевшие место при ее выполнении.

Если значение результата несущественно, то метод типа async Task предпочтительнее метода типа async void , потому что вызывающая программа получает возможность узнать о завершении операции, что упрощает упорядочение задач и обработку исключений.

Ключевое слово async указывается в объявлении метода, как public или static.
Однако спецификатор async не считается частью сигнатуры метода, когда речь заходит о переопределении виртуальных методов, реализации интерфейса или вызове.
То есть в отношении переопределения методов и реализации интерфейсов, слово async полностью игнорируется.

В объявлениях методов интерфейса слово async запрещено просто потому, что в этом нет необходимости. Если в интерфейсе объявлен метод, возвращающий тип Task , то в реализующем интерфейс классе этот метод может быть помечен словом async, а может быть и не помечен – на усмотрение программиста.

Когда поток исполнения программы доходит до оператора await, должны произойти две вещи:

1) Текущий поток должен быть освобожден, чтобы поведение программы было асинхронным. С привычной, синхронной, точки зрения это означает, что метод должен вернуть управление.

2) Когда задача Task , погруженная в оператор await, завершится, ваш метод должен продолжить выполнение с того места, где перед этим вернул управление, как будто этого возврата никогда не было.

Чтобы добиться такого поведения, метод должен приостановить выполнение, дойдя до await , и возобновить его впоследствии.

Чтобы стало яснее, сколько работы должен выполнить компилятор C#, встретив в программе оператор await , я перечислю, какие именно аспекты состояния метода необходимо сохранить.

Во-первых, запоминаются все локальные переменные метода, в том числе:
• параметры метода
• все переменные, определенные в текущей области видимости
• все прочие переменные, например счетчики циклов
• переменную this, если метод не статический

В результате после возобновления метода окажутся доступны все переменные-члены класса.
Всё это сохраняется в виде объекта в куче .NET, обслуживаемой сборщиком мусора. Таким образом, встретив await , компилятор выделяет память для объекта, то есть расходует ресурсы, но в большинстве случае это не приводит к потере производительности.
C# также запоминает место, где встретился оператор await .

контекст синхронизации, который среди прочего позволяет возобновить выполнение метода в конкретном потоке.
мое: Task помогает востанавливать контекст синхронизации, перед обратным вызовом

ExecutionContext — это родительский контекст, включающий все остальные контексты. Он не имеет собственного поведения, а служит только для запоминания и передачи контекста и используется такими компонентами .NET, как класс Task.

Оператор await можно использовать почти в любом месте метода, помеченного ключевым словом async .

Оператор await может встречаться внутри блока try , но не внутри блоков catch или finally .

В блоке catch часто, а в блоке finally всегда, исключение еще находится в фазе раскрутки стека и позже может быть возбуждено в блоке повторно.

Если использовать в этой точке await, то стек окажется другим, и определить в этой ситуации поведение повторного возбуждения исключения было бы очень сложно.
Напомню, что await всегда можно поставить не внутри блока catch, а после него, для чего следует либо воспользоваться предложением return, либо завести булевскую переменную, в которой запомнить, возбуждала ли исходная операция исключение. Например, вместо такого некорректного в C# кода:

можно было бы написать:

bool failed = false;

try
<
page = await webClient.DownloadStringTaskAsync( «https://aaa.com» );
>
catch (WebException)
<
failed = true;
>

if (failed)
<
page = await webClient.DownloadStringTaskAsync( «https://ooo.com» );
>

По завершении операции в объекте Task сохраняется информация о том, завершилась ли она успешно или с ошибкой. Получить к ней доступ проще всего с помощью свойства IsFaulted, которое равно true, если во время выполнения операции произошло исключение.
Оператор await знает об этом и повторно возбуждает исключение, хранящееся в Task.
У читателя, знакомого с системой исключений в .NET, может возникнуть вопрос, корректно ли сохраняется первоначальная трассировка стека исключения при его повторном возбуждении.

Раньше это было невозможно; каждое исключение могло быть возбуждено только один раз. Однако в .NET 4.5 это ограничение снято благодаря новому классу ExceptionDispatchInfo, который взаимодействует с классом Exception с целью запоминания трассировки стека и воспроизведения ее при повторном возбуждении.

Async-методы также знают об исключениях. Любое исключение, возбужденное, но не перехваченное в async-методе, помещается в объект Task, возвращаемый вызывающей программе. Если в этот момент вызывающая программа уже ждет объекта Task, то исключение будет возбуждено в точке ожидания. Таким образом, исключение передается вызывающей программе вместе со сформированной виртуальной трассировкой стека – точно так же, как в синхронном коде.

Я называю это виртуальной трассировкой стека, потому что стек – вообще-то принадлежность потока, а в асинхронной программе реальный стек текущего потока может не иметь ничего общего с трассировкой стека в момент исключения. В исключении запоминается трассировка стека, отражающая намерение программиста, в ней представлены те методы, который программист вызывал сам, а не детали того, как C# исполнял части этих методов в действительности.

Асинхронные методы до поры исполняются синхронно Выше я уже отмечал, что async-метод становится асинхронным , только встретив вызов асинхронного метода внутри оператора await . До этого момента он работает в том потоке, в котором вызван, как обычный синхронный метод.

Иногда длительная операция не отправляет запросов в сеть и не обращается к диску, а просто выполняет продолжительное вычисление.
Разумеется, нельзя рассчитывать на то, что поток быстро выполнит, желательно избежать зависания пользовательского интерфейса.
Для этого мы должны вернуть управление потоку пользовательского интерфейса, чтобы он мог обрабатывать другие события, а длительное вычисление производить в отдельном потоке.

Класс Task позволяет это сделать, а для обновления пользовательского интерфейса по завершении вычисления мы можем, как обычно, использовать await:

Метод Task.Run исполняет переданный ему делегат в потоке, взятом из пула ThreadPool. В данном случае я воспользовался лямбда-выражением, чтобы упростить передачу счетной задаче локальных переменных. Возвращенная задача Task запускается немедленно, и мы можем дождаться ее завершения, как любой другой задачи:

Это очень простой способ выполнить некоторую работу в фоновом потоке.
Если необходим более точный контроль над тем, какой поток производит вычисления или как он планируется, в классе Task имеется статическое свойство Factory типа TaskFactory. У него есть метод StartNew с различными перегруженными вариантами для управления вычислением:

В разделе «Task и await» выше мы видели, как просто организовать выполнение несколько параллельных асинхронных задач, – нужно запустить их по очереди, а затем ждать завершения каждой. Потом мы узнаем, что необходимо дождаться завершения каждой задачи,
иначе можно пропустить исключения.

Для решения этой задачи можно воспользоваться методом Task.
WhenAll , который принимает несколько объектов Task и порождает агрегированную задачу, которая завершается, когда завершены все исходные задачи. Вот простейший вариант метода WhenAll (имеется также перегруженный вариант для коллекции универсальных объектов Task ):

Task WhenAll (IEnumerable tasks)

Основное различие между использованием WhenAll и самостоятельным ожиданием нескольких задач, заключается в том, что WhenAll корректно работает даже в случае исключений. Поэтому старайтесь всегда пользоваться методом WhenAll .

Универсальный вариант WhenAll возвращает массив, содержащий
результаты отдельных поданных на вход задач Task. Это сделано скорее для удобства, чем по необходимости, потому что доступ к исходным объектам Task сохраняется, и ничто не мешает опросить их свойство Result , так как точно известно, что все задачи уже завершены.

Часто возникает также ситуация, когда требуется дождаться завершения первой задачи из нескольких запущенных. Например, так бывает, когда вы запрашиваете один и тот же ресурс из нескольких источников и готовы удовлетвориться первым полученным ответом.

Task > WhenAny (IEnumerable > tasks)

В ситуации, когда возможны исключения, пользоваться методом WhenAny следует с осторожностью. Если вы хотите знать обо всех исключениях, произошедших в программе,
то необходимо ждать каждую задачу, иначе некоторые исключения могут быть потеряны. Воспользоваться методом WhenAny и просто забыть об остальных задачах – всё равно, что перехватить все исключения и игнорировать их.
Это достойное порицания решение, которое может впоследствии привести к тонким ошибкам и недопустимым состояниям программы.

Метод WhenAny возвращает значение типа Task >. Это означает, что по завершении задачи вы получаете объект типа Task .
Он представляет первую из завершившихся задач и поэтому гарантированно находится в состоянии «завершен». Но почему нам возвращают объект Task, а не просто значение типа T?

Чтобы мы знали, какая задача завершилась первой, и могли отменить все остальные и дождаться их завершения.

Task > anyTask = Task.WhenAny(tasks);
Task winner = await anyTask;
Image image = await winner; // Этот оператор всегда завершается синхронно

AddAFavicon(image);
foreach (Task eachTask in tasks)
<
if (eachTask != winner)
<
await eachTask;
>
>

Нет ничего предосудительного в том, чтобы обновить пользовательский интерфейс, как только будет получен результат первой завершившейся задачи (winner), но после этого необходимо дождаться остальных задач, как сделано в коде выше. При удачном стечении обстоятельств все они завершатся успешно, и на выполнении программы это никак не отразится. Если же какая-то задача завершится с ошибкой, то вы сможете узнать причину и исправить ошибку.

Отмена асинхронных операций связывается с типом CancellationToken

По соглашению всякий метод, поддерживающий отмену, должен иметь перегруженный вариант, в котором за обычными параметрами следует параметр типа CancellationToken

При вызове метода ThrowIfCancellationRequested отмененного объекта CancellationToken возбуждается исключение типа OperationCanceledException.

Библиотека Task Parallel Library знает, что такое исключение представляет отмену, а не ошибку, и обрабатывает его соответственно. Например, в классе Task имеется свойство IsCanceled , которое автоматически принимает значение true, если при выполнении async-метода произошло исключение OperationCanceledException.

Удобной особенностью подхода к отмене, основанного на маркерах CancellationToken , является тот факт, что один и тот же маркер можно распространить на столько частей асинхронной операции, сколько необходимо, – достаточно просто передать его всем частям.
Неважно, работают они параллельно или последовательно, идет ли речь о медленном вычислении или удаленной операции, – один маркер отменяет всё.

До первого await не происходит ничего интересного.
Async не планирует выполнение метода в фоновом потоке. Единственный способ сделать это – воспользоваться методом Task.Run , который специально предназначен для этой цели, или чем-то подобным.

В приложении с пользовательским интерфейсом это означает, что код до первого await работает в потоке пользовательского интерфейса.
А в веб-приложении на базе ASP.NET – в рабочем потоке ASP.NET.

Часто бывает, что выражение в строке, содержащей первый await, содержит еще один async-метод .
Поскольку это выражение предшествует первому await, оно также выполняется в вызывающем потоке.
Таким образом, вызывающий поток продолжает «углубляться» в код приложения, пока не встретит метод, действительно возвращающий объект Task .

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

К счастью, в большинстве случаев он выполняется недолго, но важно помнить, что одно лишь наличие ключевого слова async не гарантирует отзывчивости интерфейса.

Текущий контекст SynchronizationContext – это свойство текущего потока. Идея в том, что всякий раз, как код исполняется в каком-то специальном потоке, мы можем получить текущий контекст синхронизации и сохранить его.
Впоследствии этот контекст можно использовать для того, чтобы продолжить исполнение кода в том потоке, в котором оно было начато.

Поэтому нам не нужно точно знать, в каком потоке началось исполнение, достаточно иметь соответствующий объект SynchronizationContext .

В классе SynchronizationContext есть важный метод Post , который гарантирует, что переданный делегат будет исполняться в правильном контексте.

Мы знаем, что код, предшествующий первому await , исполняется в вызывающем потоке, но что происходит, когда исполнение вашего метода возобновляется после await ?

На самом деле, в большинстве случаев он также исполняется в вызывающем потоке, несмотря на то, что в промежутке вызывающий поток мог делать что-то еще.
Это существенно упрощает написание кода.

Для достижения такого эффекта используется класс SynchronizationContext .

Важно.
В момент приостановки метода при встрече оператора await текущий контекст SynchronizationContext сохраняется.
Далее, когда метод возобновляется, компилятор вставляет вызов Post , чтобы исполнение возобновилось в запомненном контексте.

В классе Task имеется свойство Result , обращение к которому блокирует вызывающий поток до завершения задачи.

Его можно использовать в тех же местах, что await , но при этом не требуется, чтобы метод был помечен ключевым словом async или возвращал объект Task .
И в этом случае один поток занимается – на этот раз вызывающий (то есть тот, что блокируется).

Большинство написанных вами async-методов возвращают значение типа Task или Task . Цепочка таких методов, в которой каждый ожидает завершения следующего, представляет собой асинхронный аналог стека вызовов в синхронном коде.

Компилятор C# прилагает максимум усилий к тому, чтобы исключения, возбуждаемые в этих методах, вели себя так же, как в синхронном случае. В частности, блок try-catch , окружающий ожидаемый async-метод, перехватывает исключения, возникшие внутри этого метода

async Task Catcher()
<
try
<
await Thrower();
>
catch (AlexsException)
<
// Исключение будет обработано здесь
>
>

async Task Thrower()
<
await Task.Delay(100);
throw new AlexsException();
>

Для этого C# перехватывает все исключения, возникшие в вашем async-методе . Перехваченное исключение помещается в объект Task , который был возвращен вызывающей программе. Объект Task переходит в состояние Faulted. Если задача завершилась с ошибкой, то ожидающий ее метод не возобновится как обычно, а получит исключение, возбужденное в коде внутри await .

В async-методе оно возбуждается там, где находится оператор await , а не в точке фактического вызова метода. Это становится очевидным, если разделить вызов и await .

Добавить комментарий