returned error что это

Как отправить транзакцию при ошибке Error: Returned error: intrinsic gas too low

Иногда при использовании кошелька MyEtherWallet в холодном режиме появляется ошибка Error: Returned error: intrinsic gas too low:

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

Эта ошибка возникает при отправке токенов стандарта ERC даже при наличии достаточного количества эфира на кошельке. При этом увеличение цены газа (Gas Price) не помогает, так как Error появляется снова:

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

Ошибка intrinsic gas too low возникает не только в MyEtherWallet, но и в других кошельках, работающих в сети Ethereum, например, Metamask, Mist и других.

Она связана с особенностями реализации алгоритма работы сети Ethereum в конкретном кошельке. Запуск кода и отправка транзакции в сеть начинается только при наличии достаточного для этой операции количества газа. Он равен сумме обычно константного значения Gas limit (21000 Gwei) плюс комиссия за каждый байт данных, передающихся во время транзакции.

Как исправить ошибку intrinsic gas too low?

При появлении ошибки о недостаточном количестве газа для транзакции (intrinsic gas too low) нужно увеличить лимит газа (gas limit) с дефолтных 21000 до 30-40 и даже 60-70 тысяч Gwei.

При работе в кошельке MyEtherWallet (в том числе в оффлайн-версии) это делается в меню Gas Limit:

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

После увеличения Gas Limit от дефолтного значения ошибка пропадает, и транзакция передается в сеть.

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

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

Например, биржа KickEX в 2019/2020 годах осуществила airdrop своих токенов, которые частично торгуются на биржах, но большая их часть заморожена.

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

Пример ошибки, возникающей при неудачной отправке замороженных токенов Kick:

В эксплорере на etherscan при этом появляется неудачная транзакция с ошибкой Fail with error ‘SafeMath: subtraction overflow:

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

а также сообщение: Fail with error ‘SafeMath: subtraction overflow:

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

Хотя транзакция и была недачной, Gas Fee со счета отправителя все равно вычтен.

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

В случае с токенами KICK эта ситуация возникает из-за условий контракта, по которому они созданы, согласно которому владельцы не могут их тратить (по неподтвержденной информации 2 года или до KYC на их бирже).

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

Источник

error return

1 error return

2 error return

3 error return

4 error return

5 error return

6 error return

7 возврат по ошибке

См. также в других словарях:

error — er‧ror [ˈerə ǁ ˈerər] noun [countable] 1. a mistake: • The confusion was the result of a computer error. • The company has made some strategic errors. ˈcompensating ˌerror ACCOUNTING a mistake in keeping accounts that is hard to find because it… … Financial and business terms

Error code — In computer programming, error codes are enumerated messages that correspond to faults in a specific software application. They are typically used to identify faulty hardware, software, or incorrect user input in programming languages that lack… … Wikipedia

Error detection and correction — In mathematics, computer science, telecommunication, and information theory, error detection and correction has great practical importance in maintaining data (information) integrity across noisy channels and less than reliable storage… … Wikipedia

Error function — Plot of the error function In mathematics, the error function (also called the Gauss error function) is a special function (non elementary) of sigmoid shape which occurs in probability, statistics and partial differential equations. It is defined … Wikipedia

Return — The change in the value of a portfolio over an evaluation period, including any distributions made from the portfolio during that period. The New York Times Financial Glossary * * * ▪ I. return re‧turn 1 [rɪˈtɜːn ǁ ɜːrn] verb 1. [transitive]… … Financial and business terms

return — The change in the value of a portfolio over an evaluation period, including any distributions made from the portfolio during that period. Bloomberg Financial Dictionary The annual return on an investment expressed as a percentage of the total… … Financial and business terms

error — noun Etymology: Middle English errour, from Anglo French, from Latin error, from errare Date: 13th century 1. a. an act or condition of ignorant or imprudent deviation from a code of behavior b. an act involving an unintentional deviation from… … New Collegiate Dictionary

Return statement — In computer programming, a return statement causes execution to leave the current subroutine and resume at the point in the code immediately after where the subroutine was called known as its return address. The return address is saved, usually… … Wikipedia

error or mistake — A claim by the taxpayer that there has been an overpayment of tax. A formal claim has to be made within six years against the overassessment to income tax or capital gains tax resulting from an error or mistake in, or omission from, any return or … Accounting dictionary

Источник

Руководство по обработке ошибок в JavaScript

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

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

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

Этот материал, посвящённый обработке ошибок в JavaScript, разбит на три части. Сначала мы сделаем общий обзор системы обработки ошибок в JavaScript и поговорим об объектах ошибок. После этого мы поищем ответ на вопрос о том, что делать с ошибками, возникающими в серверном коде (в частности, при использовании связки Node.js + Express.js). Далее — обсудим обработку ошибок в React.js. Фреймворки, которые будут здесь рассматриваться, выбраны по причине их огромной популярности. Однако рассматриваемые здесь принципы работы с ошибками универсальны, поэтому вы, даже если не пользуетесь Express и React, без труда сможете применить то, что узнали, к тем инструментам, с которыми работаете.

Код демонстрационного проекта, используемого в данном материале, можно найти в этом репозитории.

1. Ошибки в JavaScript и универсальные способы работы с ними

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

В ходе выполнения этой команды будет создан экземпляр объекта Error и будет сгенерировано (или, как говорят, «выброшено») исключение с этим объектом. Инструкция throw может генерировать исключения, содержащие произвольные выражения. При этом выполнение скрипта остановится в том случае, если не были предприняты меры по обработке ошибки.

▍Объект Error

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

▍Генерирование и обработка ошибок

▍Конструкция try. catch

Этот блок можно использовать для обработки любых ошибок, происходящих в синхронном коде. Рассмотрим пример.

▍Блок finally

▍Асинхронные механизмы — коллбэки

▍Асинхронные механизмы — промисы

▍Асинхронные механизмы и try. catch

При таком подходе ошибки в асинхронном коде обрабатываются так же, как в синхронном. В результате теперь, при необходимости, в одном блоке catch можно обрабатывать более широкий диапазон ошибок.

2. Генерирование и обработка ошибок в серверном коде

Теперь, когда у нас есть инструменты для работы с ошибками, посмотрим на то, что мы можем с ними делать в реальных ситуациях. Генерирование и правильная обработка ошибок — это важнейший аспект серверного программирования. Существуют разные подходы к работе с ошибками. Здесь будет продемонстрирован подход с использованием собственного конструктора для экземпляров объекта Error и кодов ошибок, которые удобно передавать во фронтенд или любым механизмам, использующим серверные API. Как структурирован бэкенд конкретного проекта — особого значения не имеет, так как при любом подходе можно использовать одни и те же идеи, касающиеся работы с ошибками.

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

▍Разработка собственного конструктора объектов ошибок

▍Маршрутизация

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

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

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

Вот как выглядит код обработчика маршрутов.

Полагаем, комментарии в коде достаточно хорошо его поясняют. Надеемся, читать их удобнее, чем объяснения подобного кода, данные после него.

Теперь взглянем на файл маршрутов.

Конструктор CustomError используется так:

Это даёт следующий JSON-код, передаваемый во фронтенд:

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

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

3. Работа с ошибками на клиенте

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

▍Сохранение сведений об ошибках в состоянии приложения

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

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

Здесь будет использоваться встроенная в React система управления состоянием приложения, но, при необходимости, вы можете воспользоваться и специализированными решениями для управления состоянием — такими, как MobX или Redux.

▍Глобальные ошибки

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

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

Сообщение о глобальной ошибке

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

▍Обработка специфических ошибок, возникающих при выполнении запросов

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

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

Сообщение о специфической ошибке

▍Ошибки, возникающие в клиентской части приложения

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

returned error что это. Смотреть фото returned error что это. Смотреть картинку returned error что это. Картинка про returned error что это. Фото returned error что это

В поле ничего нет, мы сообщаем об этом пользователю

▍Интернационализация сообщений об ошибках с использованием кодов ошибок

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

Итоги

Надеемся, теперь у вас сформировалось понимание того, как можно работать с ошибками в веб-приложениях. Нечто вроде console.error(err) следует использовать только в отладочных целях, в продакшн подобные вещи, забытые программистом, проникать не должны. Упрощает решение задачи логирования использование какой-нибудь подходящей библиотеки наподобие loglevel.

Уважаемые читатели! Как вы обрабатываете ошибки в своих проектах?

Источник

С++. Что означает ошибка: [Error] ld returned 1 exit status.

Пожалуйста, скажите, в чем дело. Я новичок в программировании, чтение форумов с советами посмотреть лог сборки ничего не дало. Ниже сообщение компилятора об ошибке в полном размере и текст программы. Вместо функций прототипы, т. к. объем сообщения не позволяет вместить. (Кстати, с закомментированными функциями выдает ту же ошибку.) Пожалуйста, объясните доступно, если не тяжело.

Сообщение:
C:\Users\TATIANA\AppData\Local\Temp\ccKtnbxh.oe4.cpp:(.text+0x811): undefined reference to `doubledec(char*)’
C:\Users\TATIANA\AppData\Local\Temp\ccKtnbxh.oe4.cpp:(.text+0x81f): undefined reference to `binary(char*)’
C:\Users\TATIANA\AppData\Local\Temp\ccKtnbxh.oe4.cpp:(.text+0x83b): undefined reference to `intdec(char*)’
C:\Users\TATIANA\AppData\Local\Temp\ccKtnbxh.oe4.cpp:(.text+0x849): undefined reference to `hexadec(char*)’
C:\Users\TATIANA\Desktop\Проги\collect2.exe[Error] ld returned 1 exit status

Текст программы:
#include
#include
#include //isdigit(), isxdigit()
#include //atoi()
#include //для функции gets()
using namespace std;

int empty_str (char *s); //ПРОВЕРЯЕТ, ПУСТА СТРОКА ИЛИ НЕТ.
void format_cmd (char *s); //КОРРЕКТИРУЕТ ФОРМАТ КОМАНДЫ ДЛЯ СРАВНЕНИЯ С ЗНАЧЕНИЯМИ ИЗ ПЕРЕЧИСЛЕНИЯ.
void doubledec (char *s); //РАБОТА С ВЕЩЕСТВЕННЫМИ ЧИСЛАМИ. ПРОВЕРЯЕТ, ЧТО ЭТО DOUBLE.
void intdec (char *s); //РАБОТА С ДЕСЯТИЧНЫМИ ЧИСЛАМИ. ПРОВЕРЯЕТ, ЧТО ЭТО ДЕС. ЧИСЛО.
void binary (char *s); //РАБОТА С ДВОИЧНЫМИ ЧИСЛАМИ. ПРОВЕРЯЕТ, ЧТО ЭТО ДВ. ЧИСЛО.
void octal (char *s);
void hexadec (char *s);//РАБОТА С ЦЕЛЫМИ ШЕСТНАДЦАТЕРИЧНЫМИ ЧИСЛАМИ. ПРОВЕРЯЕТ, ЧИСЛО ЛИ ЭТО.
int is_state_change (char *s); //ПРОВЕРЯЕТ, ЕСТЬ ЛИ КОМАНДА СМЕНЫ СОСТОЯНИЯ.

class calculator <
public:
calculator (); //конструктор
double current_value;//Текущее значение

int main()<
num_sys state = dbl;
calculator calc;
char str [100];

while (1) <
cout Лучший ответ

Источник

Девиации и разветвление личности: как лечить?

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

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

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

Часть 1: обработка ошибок без ошибок

Уж давно отшумели холивары между консерваторами, возвращающими коды ошибок из функций и модернистами, что смело кидаются эксепшенами. Поэтому не будем заострять на этом внимания – все современные языки программирования умеют метать исключения и отлавливать их. Это позволяет хоть как-то «отделять зерна от плевел», бизнес-логику от обработки ошибочных ситуаций. Но, даже воспользовавшись этим несомненным благом, остается много вопросов: а как же коды ошибок? На свалку истории? Когда кидать исключения? Где ловить? Какие типы исключений нужны? Нужны ли проверяемые исключения на уровне языка?

Давайте попробуем разобраться. Самое простое это так называемы проверяемые исключения – быстрый ответ «в топку». Эксперимент в Java показал, что «овчинка выделки не стоит», загрязнение сигнатуры метода списком возможных исключений, кроме набора лишних букв не приносит никакого ощутимого профита.

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

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

Многие современные языки отказались от практики проверяемых исключений (например, Kotlin, Scala и пр.) и изначально не включали в дизайн языка их поддержку.
Значит, на каком бы современном языке вы не остановили свой выбор, у вас, скорее всего, будут под рукой непроверяемые исключения (или далее просто исключения) и механизмы их обработки. Это инструмент, осталось понять, как им ловчее пользоваться.

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

К первой категории я отношу состояние, при котором входные данные отсутствуют (например, null), не соответствуют ожиданиям (нарушают контракт) или находятся в несогласованном состоянии (противоречат друг другу). Предпринять в этом случае нечто осмысленное сложно, налицо нарушение логики, о которой следует немедленно известить создателя ПО. Назовем эту категорию логические ошибки. Для себя я дополнительно делю их на два подтипа – недопустимый аргумент, переданный в функцию/метод, либо недопустимое состояние данных объекта в целом.

Практически в любом языке уже существуют готовые исключения для этих случаев:

С++: std::invalid_argument и std::logic_error
Java: IllegalArgumentException и IllegalStateException
C#: ArgumentException и InvalidOperationException

В С/C++ существует практика покрывать такого рода ошибки ассертами. Считается, что при отладке и тестировании в отладочном варианте всплывут все ошибочные ассерты, будут исправлены, а в продакшене из соображений производительности все проверки удаляются. На мой взгляд, это рискованная идея. Надежное ПО должно контролировать свое состояние как в отладке, так и в релизном варианте. Вопрос лишь насколько параноидальными должны быть эти проверки.

Я считаю, что любой публичный метод класса (т. е. его API для общения с внешним миром) должен проверять корректность переданных ему параметров, причем делать это в первых же строчках кода. Что касается проверки состояния объекта, то его нужно проводить непосредственно перед началом работы с этим состоянием, которое должно отвечать определенным ожиданиям. Например, вызван некий метод, который работает с приватным списком и ожидает, что он не пуст – иначе запускать алгоритм не имеет смысла. Если этот список пуст значит, вероятно, не был вызван другой метод, который заполняет его данными. Эта серьезная ошибка в логике программы, так не должно было быть по замыслу автора. Бросаем исключение ошибочного состояния и пусть там «наверху» разбираются что пошло не так.

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

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

Каким же образом передать подробную информацию о сути ошибки? Можно создать иерархию исключений на каждую ситуацию, но, во-первых, это большое количество шаблонного кода, во-вторых, это всегда лень делать 😊. Лучше обратим наши взоры к старым добрым кодам ошибок (все-таки не будем их выкидывать). С помощью них можно передать конкретную причину ошибки, на основе которой приложение сможет выбрать необходимое локализованное сообщение, которое уже можно показывать пользователю. Если помимо самого кода ошибки требуется какая-то дополнительная информация (например, номер строки в котором произошла ошибка парсера ) можно отнаследоваться от базового доменного исключения с кодами ошибок и добавить все необходимые поля. Лучше всего иллюстрирует эту идею std::system_error в C++. Описание этого класса немного запутанно, поэтому ниже приведу пример, того, что я обычно делаю для домена (библиотеки).

Теперь настало время поговорить как проверять ошибки. Казалось бы, тут все очевидно:

if (что-то пошло не так) throw нечто

Если в стандартной библиотеке нет таких функция написать их совсем не сложно, вот что я использую на C++ в своих проектах:

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

Полную реализацию можно посмотреть здесь:

А вот пример использования std::system_error

Полный код примера можно посмотреть здесь:

Доменная проверка выглядит примерно так:

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

На псевдокоде это должно выглядеть так:

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

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

check(agents.isNotEmpty, “Список агентов не должен быть пустым”)

Подытожим основные моменты:

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

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

Используйте информацию из текстового поля исключения для логирования.

Передавайте специфичные для домена коды ошибок внутри исключения.

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

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

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

Для начала остановимся на трех «китах», на которых все и держится:

Создание нового потока.

Механизм эксклюзивного доступа к общим данным: мьютекс.

Возможность ожидания некоего события потоком.

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

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

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

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

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

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

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

И все же, что у нас есть по стратегии и тактике боя, как правильно готовить многопоточное блюдо чтобы кастрюля не взорвалась и больно не ударила по голове?
Адепты функциональной парадигмы, несомненно, скажут – пользуйтесь неизменяемыми объектами и будет вам счастье. Действительно с этим трудно спорить, неизменяемые объекты — это прекрасно, только… в идеальном мире, где нет низменных ограничений на объем оперативной памяти и можно бесконечно плодить новые копии коллекций для добавления очередного элемента. Но даже если абстрагироваться от конечности ресурсов: представьте, ребенку говорят, смотри, малыш, сколько замечательных игрушек мы тебе купили. Они твои. Только ими нельзя играть и нельзя их трогать – они неизменяемые, можно только смотреть глазками. Или предложат жениться на женщине, которая никогда не потеряет свою красоту, потому что она резиновая Зина, неизменяемая так сказать. Заманчиво? По мне так не очень.

В дело вступают «акторы»: эти сущности, согласно стратегии, выполняются параллельно, как и потоки, но не имеют общего состояния и могут только асинхронно обмениваться сообщениями произвольного типа. Как говориться, «нет человека – нет проблемы». Исключили общее состояние – решили проблему. Эта идея настолько понравилась создателям Scala, что они даже внесли ее реализацию прямо в язык (как, к примеру, и поддержку xml на уровне языка, а не системных библиотек).

А еще ранее, та же стратегия, только без громкого названия, была добавлена в D:

Похожий принцип лежит и в основе многопоточной модели библиотеки Qt. Там каждому объекту при рождении сохраняется идентификатор потока, в контексте которого ему посчастливилось появиться на свет. И для любых метавызовах (связывание сигнала со слотом или вызове через invokeMethod при автоматическом, используемом по умолчанию, типе связывания) происходит сравнение идентификаторов потоков вызываемой и вызывающей стороны и если они совпадают, осуществляется прямой вызов, если нет (т.е. относятся к разным потокам), вызов происходит через очередь сообщений принимающей стороны. По умолчанию, если не переопределять метод QThread::Run(), он будет «крутить» очередь сообщений и преобразовывать все входящие в вызовы соответствующих методов. Таким образом, если не нарушать принятую стратегию, методы объекта всегда вызываются только потоком его породившим. Поэтому необходимость в синхронизации данных отпадает. Можно писать код так же как в однопоточном режиме. По сути это те же «акторы», только в «профиль», немного под другим углом так сказать. Обмен сообщений скрыт внутри системы и происходит оптимизация вызовов относящихся к одному потоку (прямой вызов вместо посылки сообщения).

Казалось бы вот он Грааль. Что еще нужно? Но давайте все-таки попробуем порассуждать, может и на солнце отыщутся пятна?

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

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

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

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

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

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

Использую такой подход, отпадает необходимость в создании примитивов синхронизации (мьютексы, условные переменные) вручную, что снижает риски забыть «где-нибудь» «что-нибудь» обложить блокировкой. Мы вообще перестаем мыслить в категории блокировать/разблокировать, не должны запоминать к каким данным класса какой из мьютексов относится, мы начинаем воспринимать общие данные, объединённые логически не как разрозненные переменные, а как единый отдельный класс, к которому можно получить доступ на чтение или запись или подождать, когда он достигнет нужного нам состояния. Даже чисто визуально в коде будут четко видны намерения автора, что в данном конкретном случае он решил сотворить с общими данными. Однако иллюзия отсутствия явных блокировок может привести к неприятным эффектам, если в лямбдах доступа к общих данным мы начнем делать «что-то не, то».

Строго говоря, чтобы все работало как задумано, в функциях доступа нужно делать ровно то, для чего они создавались – запросили чтение общих данных, прочитайте, верните нужное значение и на этом все. Не нужно вызывать тяжеловесные методы каких-то «левых» классов, которые вы зачем-то решили захватить в замыкание и которые будут подключаться к удаленному хосту, а потом высчитывать факториал. Лучше ограничится прямым доступом к общим данным, которые видны внутри лямбды, а всю остальную работу проводить потом. Важно не упускать из вида, что данные, которые вернул метод доступа, сохраненные в локальной переменной уже не связаны с общим состоянием т.е. общее состояние сразу же после выхода из метода доступа может быть изменено каким-то другим потоком:

Осталось обсудить особенности объекта, который инкапсулирует в себе общие данные (т.е. пользовательский класс). В моем понимании это должен быть очень простой класс и «вещь в себе», для C++, думаю, вполне допустим обычный struct, для Java POJO и т.д. В любом случае, даже если будут использоваться методы доступа к данным, класс не должен содержать сложной/скрытой/неочевидной логики и не должен «общаться с внешним миром» (т.е. хранить ссылки на другие классы и вызывать их методы). Его назначение хранить общее состояние и не более того.

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

Вот полная реализация SharedState для C++:

А вот несложная реализация Thread Pool на C++, использующая SharedState «под капотом»:

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *