...

суббота, 28 декабря 2019 г.

[Из песочницы] ConfigureAwait: часто задаваемые вопросы

Привет, Хабр! Представляю вашему вниманию перевод статьи «ConfigureAwait FAQ» автора Стивен Тауб.

image

Async/await добавили в .NET более семи лет назад. Это решение оказало существенное влияние не только на экосистему .NET — оно также находит отражение во многих других языках и фреймворках. На данный момент реализовано множество усовершенствований в .NET с точки зрения дополнительных языковых конструкций, использующих асинхронность, реализованы API-интерфейсы с поддержкой асинхронности, произошли фундаментальные улучшения в инфраструктуре, благодаря которым async/await работает как часы (в особенности, улучшены возможности производительности и диагностики в .NET Core).

ConfigureAwait — один из аспектов async/await, который продолжает вызывать вопросы. Надеюсь, у меня получится ответить на многие из них. Я постараюсь сделать эту статью читаемой от начала до конца, и вместе с тем выполнить ее в стиле ответов на часто задаваемые вопросы (FAQ), чтобы на нее можно было ссылаться в последующем.

Чтобы на самом деле разобраться с ConfigureAwait, мы немного перенесемся назад.

Что такое SynchronizationContext?


Согласно документации System.Threading.SynchronizationContext “Обеспечивает базовую функциональность для распространения контекста синхронизации в различных моделях синхронизации”. Это определение не совсем очевидное.

В 99.9% случаев SynchronizationContext используется просто как тип с виртуальным методом Post, который принимает делегат на асинхронное выполнение (в SynchronizationContext есть и другие виртуальные члены, но они встречаются реже и не будут рассмотрены в этой статье). Метод Post базового типа буквально просто вызывает ThreadPool.QueueUserWorkItem для асинхронного выполнения предоставленного делегата. Производные типы переопределяют Post, чтобы делегат можно было выполнить в нужном месте в нужное время.

К примеру, в Windows Forms есть производный от SynchronizationContext тип, который переопределяет Post, чтобы сделать эквивалент Control.BeginInvoke. Это означает, что любой вызов данного Post-метода будет приводить к вызову делегата на более позднем этапе в потоке, связанном с соответствующим Control — так называемом UI потоке. В основе Windows Forms лежит обработка сообщений Win32. Цикл сообщений выполняется в UI потоке, который просто ждет новые сообщения для обработки. Эти сообщения вызываются движением мыши, кликом, вводом с клавиатуры, системными событиями, доступными для выполнения делегатами и т. д. Таким образом, при наличии экземпляра SynchronizationContext для UI потока в приложении Windows Forms, чтобы выполнить в нем операцию необходимо передать делегат методу Post.

В Windows Presentation Foundation (WPF) также есть производный от SynchronizationContext тип с переопределенным методом Post, который аналогично “направляет” делегат в UI поток (с помощью Dispatcher.BeginInvoke), при этом управление происходит Диспетчером WPF, а не Windows Forms Control.

И в Windows RunTime (WinRT) есть свой SynchronizationContext -производный тип, который также ставит делегат в очередь UI-потока при помощи CoreDispatcher.

Вот что скрывается за фразой “выполнить делегат в UI потоке”. Можно также реализовать свой SynchronizationContext с методом Post и какой-нибудь реализацией. Например, я могу не беспокоиться в каком потоке выполняется делегат, но я хочу быть уверен, что любые делегаты метода Post в моем SynchronizationContext выполняются с некоторой ограниченной степенью параллелизма. Можно реализовать специальный SynchronizationContext таким образом:

internal sealed class MaxConcurrencySynchronizationContext : SynchronizationContext
{
    private readonly SemaphoreSlim _semaphore;

    public MaxConcurrencySynchronizationContext(int maxConcurrencyLevel) =>
        _semaphore = new SemaphoreSlim(maxConcurrencyLevel);

    public override void Post(SendOrPostCallback d, object state) =>
        _semaphore.WaitAsync().ContinueWith(delegate
        {
            try { d(state); } finally { _semaphore.Release(); }
        }, default, TaskContinuationOptions.None, TaskScheduler.Default);

    public override void Send(SendOrPostCallback d, object state)
    {
        _semaphore.Wait();
        try { d(state); } finally { _semaphore.Release(); }
    }
}

Во фреймворке xUnit есть похожая реализация SynchronizationContext. Здесь она используется для снижения количества кода, связанного с параллельными тестами.

Преимущество здесь такие же, как и с любой абстракцией: предоставляется единый API, который можно использовать для постановки в очередь на выполнение делегата таким образом, как того пожелает программист, при этом нет необходимости знать детали реализации. Допустим, я пишу библиотеку, где мне нужно сделать некоторую работу, а затем поставить делегат в очередь обратно в исходный контекст. Для этого мне нужно захватить его SynchronizationContext, и когда я завершу необходимое, мне останется вызвать метод Post данного контекста и передать ему делегат на выполнение. Мне не нужно знать, что для Windows Forms нужно взять Control и использовать его BeginInvoke, для WPF использовать BeginInvoke у Dispatcher, или каким-то образом получить контекст и его очередь для xUnit. Все что мне нужно — это захватить текущий SynchronizationContext и использовать его позже. Для этого у SynchronizationContext есть свойство Current. Это можно реализовать следующим образом:

public void DoWork(Action worker, Action completion)
{
    SynchronizationContext sc = SynchronizationContext.Current;
    ThreadPool.QueueUserWorkItem(_ =>
    {
        try { worker(); }
        finally { sc.Post(_ => completion(), null); }
    });
}

Установить специальный контекст из свойства Current можно при помощи метода SynchronizationContext.SetSynchronizationContext.

Что такое Планировщик Задач?


SynchronizationContext это общая абстракция для “планировщика”. В некоторых фреймворках для него реализованы собственные абстракции, и System.Threading.Tasks не исключение. Когда в Task есть делегаты, которые могут быть поставлены в очередь и выполнены, они связаны с System.Threading.Tasks.TaskScheduler. Здесь также есть виртуальный метод Post для постановки делегата в очередь на выполнение (вызов делегата реализован при помощи стандартных механизмов), TaskScheduler предоставляет абстрактный метод QueueTask (вызов задачи реализован с помощью метода ExecuteTask).

Планировщик по умолчанию, который возвращает TaskScheduler.Default представляет собой пул потоков. Из TaskScheduler также есть возможность получить и переопределить методы для настройки времени и места вызова Task. Например, основные библиотеки включают тип System.Threading.Tasks.ConcurrentExclusiveSchedulerPair. Экземпляр этого класса предоставляет два свойства TaskScheduler: ExclusiveScheduler и ConcurrentScheduler. Задачи, запланированные в ConcurrentScheduler, могут выполняться параллельно, но с учетом ограничения, задаваемого ConcurrentExclusiveSchedulerPair при его создании (аналогично MaxConcurrencySynchronizationContext). Ни одна задача ConcurrentScheduler не будет выполняться, если выполняется задача в ExclusiveScheduler и разрешено запускать одновременно только одну эксклюзивную задачу. Данное поведение очень похоже на блокировку чтения/записи.

Как и SynchronizationContext, TaskScheduler имеет свойство Current, которое возвращает текущий TaskScheduler. Однако в отличие от SynchronizationContext в нем отсутствует метод для установки текущего планировщика. Вместо этого, планировщик связан с текущей задачей Task. Так, например, данная программа выведет True, так как лямбда, используемая в StartNew, выполняется в ExclusiveScheduler экземпляра ConcurrentExclusiveSchedulerPair, и TaskScheduler.Current установлен на данный планировщик:

using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        var cesp = new ConcurrentExclusiveSchedulerPair();
        Task.Factory.StartNew(() =>
        {
            Console.WriteLine(TaskScheduler.Current == cesp.ExclusiveScheduler);
        }, default, TaskCreationOptions.None, cesp.ExclusiveScheduler).Wait();
    }
}

Интересно, что TaskScheduler предоставляет статический метод FromCurrentSynchronizationContext. Метод создает новый TaskScheduler и тот ставит задачи в очередь на выполнение в возвращаемом SynchronizationContext.Current контексте, используя метод Post.

Как SynchronizationContext и TaskScheduler связаны с await?


Допустим, необходимо написать UI приложение с кнопкой. Нажатие кнопки инициирует скачивание текста с веб сайта и устанавливает его в Content кнопки. Кнопка должны быть доступна только из UI потока, в котором она и находится, поэтому, когда мы успешно загружаем дату и время и хотим разместить их в Content кнопки, нам нужно это сделать из потока, который имеет над ней контроль. Если это условие не будет выполняться, мы получим исключение:
System.InvalidOperationException: 'Вызывающий поток не может получить доступ к этому объекту, поскольку им владеет другой поток.'

Мы можем вручную использовать SynchronizationContext, чтобы установить Content в исходном контексте, например через TaskScheduler:
private static readonly HttpClient s_httpClient = new HttpClient();

private void downloadBtn_Click(object sender, RoutedEventArgs e)
{
    s_httpClient.GetStringAsync("http://example.com/currenttime").ContinueWith(downloadTask =>
    {
        downloadBtn.Content = downloadTask.Result;
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

А можем использовать SynchronizationContext напрямую:
private static readonly HttpClient s_httpClient = new HttpClient();

private void downloadBtn_Click(object sender, RoutedEventArgs e)
{
    SynchronizationContext sc = SynchronizationContext.Current;
    s_httpClient.GetStringAsync("http://example.com/currenttime").ContinueWith(downloadTask =>
    {
        sc.Post(delegate
        {
            downloadBtn.Content = downloadTask.Result;
        }, null);
    });
}

Однако оба эти варианта явно используют обратный вызов. Вместо этого мы можем использовать async/await:
private static readonly HttpClient s_httpClient = new HttpClient();

private async void downloadBtn_Click(object sender, RoutedEventArgs e)
{
    string text = await s_httpClient.GetStringAsync("http://example.com/currenttime");
    downloadBtn.Content = text;
}

Все это “просто работает” и успешно настраивает Content в UI потоке, так как в случае с вручную реализованной выше версией, по умолчанию ожидание задачи обращается к SynchronizationContext.Current и TaskScheduler.Current. Когда вы «ожидаете» что-либо в C#, компилятор преобразует код для опроса (вызовом метода GetAwaiter) “ожидаемого” (в данном случае Task) для “ожидающего” (TaskAwaiter). “Ожидающий” отвечает за присоединение коллбэка (часто называемого “продолжением”) который осуществляет обратный вызов в конечный автомат по завершении ожидания. Он реализует это, используя тот контекст/планировщик, который захватил во время регистрации коллбэка. Немного оптимизируем и настроим, получится что-то вроде такого:
object scheduler = SynchronizationContext.Current;
if (scheduler is null && TaskScheduler.Current != TaskScheduler.Default)
{
    scheduler = TaskScheduler.Current;
}

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

Что делает ConfigureAwait(false)


Метод ConfigureAwait не является специальным: он не распознается каким-либо особым образом компилятором или средой выполнения. Это обычный метод, который возвращает структуру (ConfiguredTaskAwaitable — оборачивает оригинальную задачу) и принимает булево значение. Не забывайте, что await может использоваться с любым типом, который реализует правильный паттерн. Если возвращается другой тип, это значит, что когда компилятор получает доступ к методу GetAwaiter (часть паттерна) экземпляров, но делает это из типа, возвращенного из ConfigureAwait, а не из задачи напрямую. Это позволяет менять поведение await для этого специального awaiter.

Ожидание типа, возвращаемого ConfigureAwait(continueOnCapturedContext: false) вместо ожидания Task, напрямую влияет на реализацию захвата контекста/планировщика, разобранную выше. Логика становится примерно такой:

object scheduler = null;
if (continueOnCapturedContext)
{
    scheduler = SynchronizationContext.Current;
    if (scheduler is null && TaskScheduler.Current != TaskScheduler.Default)
    {
        scheduler = TaskScheduler.Current;
    }
}

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

Почему мне нужно использовать ConfigureAwait(false)?


ConfigureAwait(continueOnCapturedContext: false) используется для предотвращения принудительного вызова коллбэка в исходном контексте или планировщике. Это дает нам несколько преимуществ:

Улучшение производительности. Существуют накладные расходы постановки обратного вызова в очередь, в отличие просто от вызова, так как для этого требуется дополнительная работа (и, как правило, дополнительная аллокация). Кроме того мы не можем использовать оптимизацию во время выполнения (мы можем оптимизировать больше, когда точно знаем, как именно будет вызван обратный вызов, но если он передан произвольной реализации абстракции, иногда это накладывает ограничения). Для высоконагруженных участков даже дополнительные затраты на проверку текущего SynchronizationContext и текущего TaskScheduler (оба из которых подразумевают и доступ к статике потоков) могут существенно увеличить накладные расходы. Если код после await не требует выполнения в исходном контексте, используя ConfigureAwait(false) можно избежать всех этих расходов, так как он не нуждается в излишней постановке в очередь, может использовать все доступные оптимизации, а также может избежать ненужного доступа к статике потока.

Предотвращение дедлоков. Рассмотрим библиотечный метод, который использует await для загрузки чего-либо из сети. Вы вызываете этот метод и синхронно блокируетесь, ожидая полного завершения задачи Task, например, с помощью .Wait() или .Result или .GetAwaiter() .GetResult(). Теперь рассмотрим, что происходит, если вызов происходит, когда текущий SynchronizationContext ограничивает число операций в нем до 1 явным образом при помощи MaxConcurrencySynchronizationContext, или неявно, если это контекст с единственным потоком для использования, (например потоком UI). Таким образом, вы вызываете метод в единственном потоке, а затем блокируете его, ожидая завершения операции. Происходит запуск загрузки по сети и ожидание ее завершения. По умолчанию ожидание Task захватит текущий SynchronizationContext (так и в этом случае), и по завершении загрузки из сети, оно помещается в очередь обратно в коллбэк SynchronizationContext, который вызовет оставшуюся часть операции. Но единственный поток, который может обработать обратный вызов в очереди, в настоящее время заблокирован в ожидании завершения операции. И эта операция не будет завершена, пока не будет обработан обратный вызов. Дедлок! Он может произойти даже в том случае, когда контекст не ограничивает параллелизм до 1, но каким-либо образом ограничены ресурсы. Представьте себе ту же ситуацию, только со значением 4 для MaxConcurrencySynchronizationContext. Вместо того чтобы выполнить операцию однократно, мы ставим в очередь к контексту 4 вызова. Каждый вызов производится и происходит блокировка в ожидании его завершения. Все ресурсы теперь заблокированы в ожидании завершения асинхронных методов, и единственное, что позволит их завершить, это если их коллбэки будут обработаны этим контекстом. Однако тот уже полностью занят. Снова дедлок. Если бы вместо этого библиотечный метод использовал ConfigureAwait(false), он не ставил бы обратный вызов в очередь к исходному контексту, что позволило бы избежать сценариев дедлока.

Нужно ли использовать ConfigureAwait (true)?


Нет, за исключением тех случаев, когда нужно явно указать, что вы не используете ConfigureAwait(false) (например, для скрытия предупреждений статического анализа и т.п.). ConfigureAwait(true) не делает ничего значимого. Если сравнить await task и await task.ConfigureAwait(true) — они окажутся функционально идентичны. Таким образом, если в коде присутствует ConfigureAwait(true), его можно удалить без каких-либо негативных последствий.

Метод ConfigureAwait принимает логическое значение, так как в некоторых ситуациях ему может потребоваться передача переменной для управления конфигурацией. Но в 99% случаев задается значение false, ConfigureAwait(false).

Когда использовать ConfigureAwait(false)?


Это зависит от того, реализуете ли вы код уровня приложения или код библиотеки общего назначения.

При написании приложений обычно требуется некоторое поведение по умолчанию. Если модель приложения/среда (например, Windows Forms, WPF, ASP.NET Core) публикует специальный SynchronizationContext, почти наверняка этому есть веская причина: значит, код позволяет заботиться о контексте синхронизации для правильного взаимодействия с моделью приложения/средой. Например, если вы пишете, обработчик событий в приложении Windows Forms, тест в xUnit, или код в контроллере ASP.NET MVC, независимо от того, опубликовала ли модель приложения SynchronizationContext, вам нужно использовать SynchronizationContext при его наличии. Это значит, если используются ConfigureAwait(true) и await, обратные вызовы/продолжения отправляются обратно в исходный контекст — все идет как нужно. Отсюда можно сформулировать общее правило: если вы пишете код уровня приложения, не используйте ConfigureAwait(false). Давайте вернемся к обработчику клика:

private static readonly HttpClient s_httpClient = new HttpClient();

private async void downloadBtn_Click(object sender, RoutedEventArgs e)
{
    string text = await s_httpClient.GetStringAsync("http://example.com/currenttime");
    downloadBtn.Content = text;
}

downloadBtn.Content = text должен быть выполнен в исходном контексте. Если код нарушил это правило и вместо этого использовал ConfigureAwait (false), тогда он не будет использован в исходном контексте:
private static readonly HttpClient s_httpClient = new HttpClient();

private async void downloadBtn_Click(object sender, RoutedEventArgs e)
{
    string text = await s_httpClient.GetStringAsync("http://example.com/currenttime").ConfigureAwait(false); // баг
    downloadBtn.Content = text;
}

это приведет к неправильному поведению. То же самое относится и к коду в классическом ASP.NET приложении, зависящем от HttpContext.Current. При использовании ConfigureAwait(false) последующая попытка использовать функцию Context.Current, скорее всего, приведет к проблемам.

Этим и отличаются библиотеки общего назначения. Они являются универсальными отчасти потому, что их не волнует среда, в которой они используются. Вы можете использовать их из веб-приложения, из клиентского приложения или из теста — это не имеет значения, так как код библиотеки является агностическим для модели приложения, в которой он может быть использован. Агностический также означает, что библиотека не будет делать что-либо для взаимодействия с моделью приложения, например, она не будет получать доступ к элементам управления пользовательского интерфейса, потому что библиотека общего назначения ничего о них не знает. Так как нет необходимости запускать код в какой-либо конкретной среде, мы можем избежать принудительного вызова продолжений/обратных вызовов к исходному контексту, и мы делаем это, используя ConfigureAwait(false), что дает нам преимущества в производительности и повышает надежность. Это приводит нас к следующему: если вы пишете код библиотеки общего назначения, используйте ConfigureAwait(false). Вот почему каждый (или почти каждый) await в библиотеках среды выполнения .NET Core использует ConfigureAwait(false); За несколькими исключениями, которые скорее всего являются багами, и будут исправлены. Например, этот PR исправил отсутствующий вызов ConfigureAwait(false) в HttpClient.

Конечно это не везде имеет смысл. Например, одним из больших исключений (или, по крайней мере, случаев, где нужно подумать) в библиотеках общего назначения является случай, когда эти библиотеки имеют API, которые принимают делегаты на вызов. В таких случаях, библиотека принимает потенциальный код уровня приложения от вызывающей стороны, что делает эти допущения для библиотеки ”общего назначения" весьма спорными. Представьте, например, асинхронную версию метода Where LINQ: public static async IAsyncEnumerable<T> WhereAsync(this IAsyncEnumerable<T> source, Func<T, bool> predicate). Должен ли predicate вызываться в исходном SynchronizationContext вызывающего кода? Это зависит от реализации WhereAsync, и это причина, по которой он может решить не использовать ConfigureAwait(false).

Даже в особых случаях придерживайтесь общей рекомендации: используйте ConfigureAwait(false) если вы пишете библиотеку общего назначения/app-model-agnostic код.

Гарантирует ли ConfigureAwait (false), что обратный вызов не будет выполнен в исходном контексте?


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

Можно ли использовать ConfigureAwait (false) только при первом ожидании в моем методе, а в остальных — нет?


В общем, нет. Вспомните предыдущий FAQ. Если await task.ConfigureAwait(false) включает задачу, которая уже выполнена к моменту ожидания (что на самом деле происходит довольно часто), тогда использование ConfigureAwait(false) будет бессмысленным, так как поток продолжает выполнять следующий код в методе и по-прежнему в том же контексте, что и был ранее.

Одно примечательное исключений в том, что первый await всегда будет завершаться асинхронно, и ожидаемая операция вызовет его обратный вызов в среде, свободной от специального SynchronizationContext или TaskScheduler. Например, CryptoStream в библиотеках среды выполнения .NET проверяет, что его потенциально интенсивный с точки зрения вычислений код не выполняется как часть синхронного вызова вызывающего кода. Для этого он использует специальный awaiter, чтобы убедиться, что код после первого ожидания выполняется в потоке пула потоков. Однако даже в этом случае можно заметить, что следующий await по-прежнему использует ConfigureAwait(false); Технически в этом нет необходимости, но это значительно упрощает ревью кода, так как не нужно разбираться, почему не был использован ConfigureAwait(false).

Можно ли использовать Task.Run, чтобы избежать использования ConfigureAwait (false)?


Да, если вы напишете:
Task.Run(async delegate
{
    await SomethingAsync(); // не увидит оригинальный контекст
});

тогда ConfigureAwait(false) в SomethingAsync() будет лишним, так как делегат, переданный в Task.Run будет выполнен в потоке пула потоков, так что без изменений в коде выше, SynchronizationContext.Current вернет значение null. Более того, Task.Run неявно использует TaskScheduler.Default, поэтому TaskScheduler.Current внутри делегата также вернет значение Default. Это значит, что await будет иметь такое же поведение независимо от того, был ли использован ConfigureAwait(false). Это также не может дать гарантии насчет того, что может делать код внутри данной лямбды. Если у вас есть код:
Task.Run(async delegate
{
    SynchronizationContext.SetSynchronizationContext(new SomeCoolSyncCtx());
    await SomethingAsync(); // будет нацелен на SomeCoolSyncCtx
});

тогда код внутри SomethingAsync фактически увидит SynchronizationContext.Current экземпляра SomeCoolSyncCtx . и этот await, и любые не настроенные ожидания внутри SomethingAsync будут возвращены в данный контекст. Таким образом, чтобы использовать этот подход, необходимо понимать, что может делать или не делать весь код, который вы ставите в очередь, и могут ли его действия стать помехой.

Этот подход также происходит за счет необходимости создания/постановки в очередь дополнительного объекта задачи. Это может иметь или не иметь значение для приложения/библиотеки в зависимости от требований к производительности.

Также имейте в виду, что такие обходные пути могут вызвать больше проблем, чем преимуществ, и иметь разные непреднамеренные последствия. Например, некоторые инструменты статического анализа помечают флагом ожидания, которые не используют ConfigureAwait(false) CA2007. Если вы включите анализатор, а затем используете такой трюк чтобы избежать использования ConfigureAwait, есть большая вероятность, что анализатор отметит его. Это может повлечь за собой еще больше работы, например, вы можете захотеть отключить анализатор из-за его назойливости, а это уже повлечет за собой пропуск других мест в кодовой базе, где на самом деле нужно использовать ConfigureAwait(false).

Можно ли использовать SynchronizationContext.SetSynchronizationContext, чтобы избежать использования ConfigureAwait (false)?


Нет. Хотя, возможно. Это зависит от используемой реализации

Некоторые разработчики делают так:

Task t;
SynchronizationContext old = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
try
{
    t = CallCodeThatUsesAwaitAsync(); // await'ы здесь не увидят оригинальный контекст
}
finally { SynchronizationContext.SetSynchronizationContext(old); }
await t; // будет по-прежнему нацелен на исходный контекст

в надежде, что это заставит код внутри CallCodeThatUsesAwaitAsync рассматривать текущий контекст как null. Так и будет. Однако этот вариант не повлияет на то, какой await видит TaskScheduler.Current. Поэтому если код выполняется в специальном TaskScheduler, await’ы внутри CallCodeThatUsesAwaitAsync будут видеть и становиться в очередь к этому специальному TaskScheduler.

Как и в Task.Run FAQ, здесь применимы все те же оговорки: есть определенные последствия такого подхода, и код внутри блока try может также помешать этим попыткам, задав другой контекст (или вызывая код с помощью нестандартного планировщика задач).

При таком шаблоне также нужно быть осторожным с незначительными изменениями:

SynchronizationContext old = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
try
{
    await t;
}
finally { SynchronizationContext.SetSynchronizationContext(old); }

Видите в чем проблема? Немного трудно заметить, но это впечатляет. Нет гарантии, что ожидание в итоге вызовет обратный вызов/продолжение в исходном потоке. Это значит, что возврат SynchronizationContext к исходному может не произойти в первоначальном потоке, что может привести к тому, что последующие рабочие элементы в этом потоке увидят неправильный контекст. Для противодействия этому, хорошо написанные модели приложений, которые задают специальный контекст, как правило, добавляют код для ручного сброса его перед вызовом любого дополнительного пользовательского кода. И даже если это происходит в одном потоке, может понадобиться некоторое время, в течение которого контекст может не быть соответствующим образом восстановлен. А если он работает в ином потоке, это может привести к установке неправильного контекста. И так далее. Довольно далеко от идеала.

Нужно ли использовать ConfigureAwait(false) если я использую GetAwaiter ().GetResult ()?


Нет. ConfigureAwait затрагивает только коллбэки. В частности, шаблон awaiter требует, чтобы awaiter’ы предоставляли свойство IsCompleted, методы GetResult и OnCompleted (опционально с методом UnsafeOnCompleted). ConfigureAwait влияет только на поведение {Unsafe}OnCompleted, так что если вы напрямую вызываете GetResult(), независимо от того делаете это через TaskAwaiter или ConfiguredTaskAwaitable.ConfiguredTaskAwaiter разницы в поведении нет. Поэтому если вы видите task.ConfigureAwait(false).GetAwaiter().GetResult() вы можете заменить его на task.GetAwaiter().GetResult() (кроме того подумайте, действительно ли вам нужна именно такая реализация).

Я знаю, что код выполняется в среде, в которой никогда не будет специального SynchronizationContext или специального TaskScheduler. Можно ли не использовать ConfigureAwait(false)?


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

Я слышал, что в .NET Core нет необходимости применять ConfigureAwait (false). Так ли это?


Не так. Она необходима при работе в .NET Core по тем же причинам, что и при работе в .NET Framework. В этом плане ничего не изменилось.

Изменилось то, публикуют ли определенные среды собственный SynchronizationContext. В частности, в то время как классический ASP.NET в .NET Framework имеет свой SynchronizationContext, у ASP.NET Core его нет. Это означает, что код, запущенный в приложении ASP.NET Core по умолчанию не будет видеть специальный SynchronizationContext, что уменьшает необходимость в ConfigureAwait(false) в данной среде.

Однако это не значит, что никогда не будет присутствовать пользовательский SynchronizationContext или TaskScheduler. Если какой-либо код пользователя (или другой код библиотеки, используемый приложением) задает пользовательский контекст и вызывает ваш код или вызывает ваш код в Задаче, запланированной в специальном планировщике задач, тогда await’ы в ASP.NET Core будут видеть нестандартный контекст или планировщик, который может привести к необходимости использования ConfigureAwait(false). Конечно, в ситуациях, когда вы избегаете синхронных блокировок (что в любом случае нужно делать в веб-приложениях) и если вы не против небольших накладных расходов в производительности в некоторых случаях, вы можете обойтись без использования ConfigureAwait(false).

Могу ли я использовать ConfigureAwait, когда «ожидаю выполнения foreach» над IAsyncEnumerable?


Да. Пример см. в статье MSDN.

Await foreach соответствует шаблону и, таким образом, может использоваться для перечисления в IAsyncEnumerable<T>. Он также может использоваться для перечисления элементов, которые представляют правильную область API. Библиотеки времени выполнения .NET включают метод расширения ConfigureAwait для IAsyncEnumerable<T>, который возвращает специальный тип, который оборачивает IAsyncEnumerable<T> и Boolean и соответствует правильному шаблону. Когда компилятор генерирует вызовы к MoveNextAsync и DisposeAsync перечислителя. Эти вызовы относятся к возвращенному сконфигурированному типу структуры перечислителя, который в свою очередь, выполняет ожидания нужным образом.

Можно ли использовать ConfigureAwait, при ‘await using’ IAsyncDisposable?


Да, пусть и с небольшим усложнением.

Как и с IAsyncEnumerable<T>, .NET библиотеки времени выполнения предоставляют метод расширения ConfigureAwait для IAsyncDisposable и await using, будет отлично работать, поскольку он реализует соответствующий шаблон (а именно, предоставляет соответствующий метод DisposeAsync):

await using (var c = new MyAsyncDisposableClass().ConfigureAwait(false))
{
    ...
}

Проблема здесь состоит в том, что тип c — теперь не MyAsyncDisposableClass, а скорее System.Runtime.CompilerServices.ConfiguredAsyncDisposable, который возвратился из метода расширения ConfigureAwait для IAsyncDisposable.

Чтобы обойти это, нужно добавить строку:

var c = new MyAsyncDisposableClass();
await using (c.ConfigureAwait(false))
{
    ...
}

Теперь тип c снова является желаемым MyAsyncDisposableClass. Что также имеет эффект увеличения области действия для c; если нужно, вы можете обернуть все это в фигурные скобки.

Я использовал ConfigureAwait (false), но мой AsyncLocal все равно перетек в код после ожидания. Это баг?


Нет, это вполне ожидаемо. Поток данных AsyncLocal<T> являются частью ExecutionContext, который отделен от SynchronizationContext. Если вы явно не отключили поток ExecutionContext с помощью ExecutionContext.SuppressFlow(), ExecutionContext (и, таким образом, данные AsyncLocal <T>) всегда будет проходить через awaits, независимо от того, используется ли ConfigureAwait во избежание захвата исходного SynchronizationContext. Более подробно рассмотрено в этой статье.

Могут ли языковые средства помочь мне избежать необходимости явно использовать ConfigureAwait(false) в моей библиотеке?


Разработчики библиотек иногда выражают недовольство необходимостью использовать ConfigureAwait(false) и просят менее инвазивные альтернативы.

В настоящее время их нет, по крайней мере, они не встроены в язык/компилятор/среду выполнения. Однако существует множество предложений относительно того, как это можно реализовать, например: 1, 2, 3, 4.

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

Let's block ads! (Why?)

[Из песочницы] Приготовься к введению в России социального рейтинга

image

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

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

Предварительные действия


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

Классика


Для начала разберемся в возможных действиях, которые помогут сохранить приватность информации на устройстве. Как правило выделяют:
  1. Управление доступом приложений.
  2. Блокировка рекламы и отслеживающих трекеров.
  3. Установка файрвола.
  4. Изоляция приложений.

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

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

F- Droid


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

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

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

  1. Замена закрытых программ на этичные аналоги из магазины F- Droid.
  2. Удаление ненужных предустановленных программ (что также повысит быстродействие устройства).
  3. Установка файрвола NetGuard.
  4. Изоляция закрытых приложений в песочницах (например, «антивируса» Сбербанк Онлайн).
  5. Использование веб- интерфейса для социальных сетей.

1. Замена закрытых программ на этичные аналоги из магазины F- Droid


С официального сайта F- Droid скачиваете программу в виде apk- файла и устанавливаете его на свое устройство (возможно в настройках придется разрешить установку из непроверенных источников). Например, я использую такие программы:
AnySoftKeyboard — клавиатура без прав на доступ в интернет.

NetGuard — файрвол (о нем более подробно будет написано ниже).

NewPipe — клиент youtube с возможностью вопроизведения в фоне и «мягкой» настройкой скорости воспроизведения и тона (с точностью до 1%). В настройках задайте значения полей «Страна контента по умолчанию» и «Язык контента по умолчанию» для получения подходящей инфорации. Поддерживается импорт / экспорт каналов из стандартного приложения youtube.

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

Librera PRO — чтение книг вслух разного формата (в том числе с возможностью выбора позиции начала чтения).

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

VLC — потрясающе качественный видео- проигрыватель: может играть видео в аудио- режиме, ускорять / замедлять воспроизведение, с помощью свайпов меняет яркость / громкость…

Riseup VPN — vpn- клиент.

Tor Browser — единственное спасение от чтения файла proc/net- уязвимости всех андроид устройств до 10 версии (любое приложение с любыми правами может читать этот файл, в котором отражаются все посещенные вами ip- адреса).

Orbot — позволяет пустить трафик любых приложений через тор- сеть.

Open Camera — выполнение фото / видео съемки с устройства.

Files — файловый менеджер.

QKSMS — клиент для работы с SMS сообщениями.

Больше этичных приложений рассмотрено по этой ссылке:Обзор свободного ПО для Android.

2. Удаление ненужных предустановленных программ (что также повысит быстродействие устройства)


Для выпонения этого пункта будем использовать ADB (Android Debug Bridge). Сначала на ПК необходимо установить необходимые компоненты. Подробно для разных ОС этот вопрос рассмотрен в статье Команды adb для Android пользователей. Далее получаем список подключенных устройств:
adb devices -l

, и в случае успешного результата приступаем к удалению пакетов:
adb shell pm uninstall --user 0 ИМЯ_ВАШЕГО_ПАКЕТА

Программы, которые мы хотим удалить, необходимо задавать в виде пакетов, названия которых можно узнать с помощью программы App Inspector из Play Маркет. Например, я удалил следующие приложения:

Видео, Диск, Заметки, Интернет (браузер от Samsung), Карты, Клавиатура Samsung, Магазин, Погода, Редактор сообщений электронной почты, руководство пользователя, хранилище электронной почты, AllShare, BBCAgent, Email, ELM Agent, Facebook (+ installer и manager), Google Play Музыка, Google Play Фильмы, Hangouts, KLMS Agent, KNOX (+ client и agent), MegaFonPRO, Samsung Account, Samsung Billing, Samsung Cloud Data Relay, Samsung Memo Sync, Samsung Syncadapters, Теле2 меню, UBank.

Отдельно замечу, что KLMS Agent, KNOX — полезные функции, если они действительно поддерживаются вашим устройством и работодателем. Это разработка Samsung, которая позволяет изолировать приложения работодателя и настраивается системным администратором удаленно. Поэтому перед удалением проверьте работает ли эта технология на вашем устройстве (возможно будущему работодателю она будет необходима). Ниже я приведу этичный аналог Shelter, который полностью настраивается самим пользователем, и полезен при использовании закрытых программ.

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

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

После удаления этих приложени на моем устройстве освободилось 2,5 ГБ на диске, а оперативная память стала заполнена только на 2/3 (ранее все было занято под завязку). Смартфон стал значительно отзывчивее, а приложения не закрываются при переключении между ними.

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

3. Установка файрвола NetGuard


После того как мы освободили большое количество ресурсов настало время ставить файрвол. Дело в том, что без root- прав он реализован в виде отдельного приложения постоянно висящего в памяти (а не в качестве редактора системного файла iptables), и менеджер ресурсов при недостатке памяти его автоматически закрывает. Также для NetGuard необходимо отключить оптимизацию энергопотребления: Настроки -> Батарея -> Оптимизация приложений (кнопка «Подробно») -> отключаем для приложения NetGuard.

NetGuard имеет русскоязычный интерфес, что облегчает его настройку:
По умолчанию: обязательно включите 3 опции «Блокировать wi-fi / моб.сеть / роуминг по умолчанию».

Опции: выберите «Автозапуск через 1 минуту».
Параметры сети: включите «Блокировать wi-fi / моб. сети».

Теперь в основном окне программы укажите только те приложения, которым реально нужен выход в интернет. Например, в моем случае это DuckDuckGo, F-Droid, NewPipe, RiseupVPN и TorBrowser. К сожалению NetGuard не может ограничить интернет для системных приложений (например, Play Маркет), но в п.2 мы удалили все ненужные приложения, поэтому утечки будут только в компанию Google.

Приложение работает с помощью использования vpn- сервиса устройства, за который оно конкурирует с vpn- клиентами (например, с Riseup VPN). Т.е. при включении стороннего VPN- клиента файрвол будет выключаться, и наоборот- включение NetGuard выключит соединение с помощью VPN- клиента. Поэтому вместо VPN- клиентов желательно использовать сеть Tor (Tor Browser и Orbot).

4. Изоляция закрытых приложений в песочницах


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

5. Использование веб- интерфейса для социальных сетей


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

Итоги


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

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

Let's block ads! (Why?)

Трейдеры получили доступ к пресс-конференциям Центробанка Англии до их публичной трансляции

Изображение: Unsplash

Власти Великобритании расследуют утечку информации, которая могла предоставить одним из участников фондового рынка преимущество над другими. Трейдеры смогли получить доступ к записям пресс-конференций Центробанка Англии (Bank of England) до старта их официальных трансляций.

Что произошло


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

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

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

Как произошла утечка


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

За видеопоток с пресс-конференций отвечает агентство Bloomberg, но его специалисты не занимались организацией «запасной» аудиозаписи.

Как инвесторам защититься от манипуляций на рынках


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

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

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

Работает это так: в структурный продукт «собираются» активы с низким риском и небольшой возможной прибылью и более рискованные активы, которые при удачном стечении обстоятельств могут принести более высокий доход. Идея проста – если рискованный инструмент «не сработает» и по нему будет убыток, то он будет компенсирован прибылью от менее рискованного актива – поэтому его объем в составе структурного продукта выше.

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

Полезные ссылки по теме инвестиций и биржевой торговли:


Читайте обзоры, аналитику рынков и инвестидеи в Telegram-канале ITI Capital

Let's block ads! (Why?)

[Из песочницы] Почему Rust должен стать функциональным языком программирования

Привет, Хабр!

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

  1. Для императивной реализации — выигрыш от Rust получился всего 20 %. Это означает, что JVM вплотную приблизилась к нативной производительности, и тут уже нечего улучшать.
  2. Для функциональной реализации — Rust оказался быстрее в 4.5 раза, потребление памяти снизилось в 5.5 раза, а отсутствие сборщика мусора сделало программу более стабильной (меньше разброс показателей). Это интересно для тех, кто хочет писать быстрые функциональные программы.
  3. Концепция единственного владельца данных (и единственной мутабельной ссылки), принятая в Rust, очень близка концепции иммутабельности, в результате чего функциональные алгоритмы, основанные на неизменяемости, рекурсии и копировании, легко ложатся на Rust практически без переписывания, тогда как императивные алгоритмы заставляют редизайнить код, учитывать мутабельность ссылок, времена жизни, и т.д.

Вывод — Rust как будто специально создан для ФП, хотя возможности его синтаксиса пока не дотягивают до Scala.
Итак, начнем со Scala и реализуем быструю сортировку в 2-х стилях:

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

def sort_imp(arr: Array[Double]) {
  def swap(i: Int, j: Int) {
    val t = arr(i)
    arr(i) = arr(j)
    arr(j) = t
  }

  def sort1(l: Int, r: Int) {
    val pivot = arr((l + r) / 2)
    var i = l
    var j = r
    while (i <= j) {
      while (arr(i) < pivot) i += 1
      while (arr(j) > pivot) j -= 1
      if (i <= j) {
        swap(i, j)
        i += 1
        j -= 1
      }
    }
    if (l < j) sort1(l, j)
    if (j < r) sort1(i, r)
  }

  sort1(0, arr.length - 1)
}

Функционально — используем рекурсивную функцию, которая создает три новых массива, и затем возвращает их объединение. Очень компактный и красивый код, практически негде ошибиться, но очень медленный и прожорливый (бедный, бедный GC).
def sort_fun(arr: Array[Double]): Array[Double] = {
  if (arr.length <= 1) 
    arr
  else {
    val pivot = arr(arr.length / 2)
    Array.concat(
      sort_fun(arr filter (pivot > _)),
      arr filter (pivot == _),
      sort_fun(arr filter (pivot < _))
    )
  }
}

Бенчмаркинг ($ sbt run) на массиве 10 млн. случайных чисел и моем дохлом ноутбуке:
  • императивно — 2.5 секунды
  • функционально — 40 секунд

Как правильно считать память приложения я не знаю, но сам процесс java занял 3.6 Гб.

Перепишем то же самое на Rust:

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

fn sort_imp(arr: &mut Vec<f64>) {
  fn swap(arr: &mut Vec<f64>, i: usize, j: usize) {
    let t = arr[i];
    arr[i] = arr[j];
    arr[j] = t;
  };

  fn sort1(arr: &mut Vec<f64>, l: usize, r: usize) {
    let pivot = arr[(l + r) / 2];
    let mut i = l;
    let mut j = r;
    while i <= j {
      while arr[i] < pivot { i += 1; }
      while arr[j] > pivot { j -= 1; }
      if i <= j {
        swap(arr, i, j);
        i += 1;
        j -= 1;
      }
    }
    if l < j { sort1(arr, l, j); }
    if j < r { sort1(arr, i, r); }
  };

  sort1(arr, 0, arr.len() - 1);
}

Функционально — мы видим, что алгоритм идентичен, однако синтаксис Rust хуже приспособлен для функциональщины, пришлось объявлять промежуточный вектор.

Последовательное применение iter() и filter() к элементам массива приводит к тому, что параметр замыкания x получает тип &&f64, и двойную ссылку приходится разыменовывать **. Нужно явно клонировать отфильтрованные значения. Сигнатура функции append() требует именно мутабельную ссылку, а не сам вектор, что также увеличивает количество букв.

fn sort_fun(arr: Vec<f64>) -> Vec<f64> {
  if arr.len() <= 1 {
    arr
  } else {
    let pivot = arr[arr.len() / 2];
    let mut a = Vec::<f64>::with_capacity(arr.len());
    a.append(&mut sort_fun(arr.iter().filter(|x| pivot > **x).cloned().collect()));
    a.append(&mut arr.iter().filter(|x| pivot == **x).cloned().collect());
    a.append(&mut sort_fun(arr.iter().filter(|x| pivot < **x).cloned().collect()));
    a
  }
}

Можно было бы попытаться сделать код красивее, применив вместо объединения массивов — объединение итераторов iter().filter(...).chain(...). Это был бы полный zero-cost, но в итераторе мы не можем определить количество элементов, значит не можем поделить его пополам, а значит — реализовать алгоритм. Но в других случаях ленивые итераторы это красиво, экономно и быстро.

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

Бенчмаркинг ($ cargo build --release):

  • императивно — 2 секунды
  • функционально — 9 секунд

Максимальное потребление памяти — 650 Мб.

В сухом остатке:

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

В мире есть много быстрых и выразительных ООП-языков, а по настоящему быстрых zero-cost функциональных языков — не очень. И если Rust будет двигаться в этом направлении — возможно ФП-подход найдет больше сторонников. Тем более, что по итогам 2019 года и Scala и Rust показали существенный рост популярности на фоне спада мейнстримных языков.

Полный текст на Scala.
Полный текст на Rust.

Спасибо за внимание.

Let's block ads! (Why?)

Американский кардиолог подал в суд на Apple из-за использования компанией его патента

Кардиолог Джозеф Визель (Joseph Wiesel) и наручные часы с дополнительной функциональностью Apple Watch.

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

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

Аналогичный способ используется в Apple Watch. Так как оптический пульсометр Apple Watch также работает на основе фотоплетизмографии. Из описания механизма измерения пульса с помощью Apple Watch:
Красный цвет крови обусловлен тем, что она отражает красный свет и поглощает зеленый. Благодаря зеленым светодиодам в паре со светочувствительными фотодиодами часы Apple Watch могут замерять объем крови, проходящий через запястье в каждый момент времени. При ударе сердца объем потока крови в запястье (а с ним и объем поглощенного зеленого света) увеличивается, а в период между ударами уменьшается. Светодиоды Apple Watch — мигают с частотой несколько сотен раз в секунду, благодаря чему часы могут измерить количество ударов сердца в минуту, то есть ваш пульс. Оптический пульсометр может измерить пульс в диапазоне от 30 до 210 сердечных сокращений в минуту. Кроме того, он способен компенсировать слабый сигнал за счет увеличения яркости светодиодов и частоты дискретизации.

Кардиолог Джозеф Визель рассказал, что в сентябре 2017 года связывался с Apple по этому поводу и предоставлял компании дополнительную детальную информацию о своем патенте. Джозеф Визель также предупреждал Apple, что используемый в настоящее время в Apple Watch алгоритм отслеживания пульса нерегулярного типа работает некорректно, часто округляя показания, что может вредить пользователям при анализе этих данных. Таким образом, он просил компанию согласовать использование его патента в гаджетах и следовать его четким предписаниям для получения в Apple Watch более корректных данных по анализу пульса пользователя. Однако в Apple отказались от сотрудничества с ним по данному вопросу и никак не отреагировали на ситуацию с незаконным использованием патента Джозефа Визеля в механизме измерения пульса с помощью Apple Watch, продолжив выпускать новые модели этих устройств.

Теперь же, 27 декабря 2019 года Джозеф Визель подал иск в суд против Apple, где утверждает, что ответчик (Apple) преднамеренно использовал его патент (U.S. Patent No. 7020514). Джозеф Визель будет добиваться запрета на использование в устройствах Apple механизма на основе его патента без разрешения, а также хочет получить денежную компенсацию за нарушение его авторских прав, причем по максимальной ставке, разрешенной законом о возмещении ущерба. В настоящее время в компании Apple данную ситуацию с кардиологом и его иском не прокомментировали.

Let's block ads! (Why?)

[Перевод] Путеводитель по «летающим автомобилям»

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

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

Несущие винты или винты с фиксированным крылом


image

Мультироторный eHang на 2 человек, представлен в 2017. Уже летает

В настоящее время мультироторный дрон не вызывает больше никаких вопросов и проблем. Мы знаем, как его построить. Он не очень эффективен, поэтому его дальность полета ограничена батарейками. Поскольку с ним уже разобрались, дроны стали первыми устройствами, способными взлетать. Ими являются чистые мультикоптеры, такие как Volocopter (с 18 роторами) и eHang. Существует еще одна подкатегория: «Hoverbikes» («летающие мотоциклы»), которые еще больше похожи на потребительский беспилотник, поскольку у них есть только одно сиденье, которое часто располагается над винтами. Сюда относят первый автомобиль Kitty Hawk, «Flyer».
Его намного проще сделать, поэтому о нем больше упоминаний в прессе. Его можно рассматривать как увеличение мультикоптера, чтобы получилось нечто, что может выдержать 60-120 кг нагрузки и летать в течение 30 минут. Однако существует предел, по мере того, как батарея становится больше (чтобы выдерживать больше нагрузки и преодолевать большее расстояние), она становится тяжелее, уменьшая тем самым допустимую нагрузку и дальность полета при одновременном увеличении стоимости.

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

image

Volocopter дроноподобный транспорт, который уже летает. Радиус полета меньше, но и дизайн проще.

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

Конвертоплан (и прочие отклоняемые тяги)


image

Тестовый полет Airbus Vahana с фиксированным положением винта и отклоняемым крылом.

Самый старый дизайн для самолетов с двумя роторами и крылом является отклоняемый ротор. Большие роторы направлены вверх и могут быть отклонены вперед. V22-Osprey печально известен из-за подобной конструкции, ставшей причиной смертельных аварий.

Тем не менее, знания полученные при строительстве данной конструкции в Bell Helicopter привели к дизайну Nexus, в котором использовались отклоняемые закрытые винты. Другие конструкции устанавливают роторы на крыло и наклоняют всю сборку крыла. Lilium Jet помещает их в хвостовую часть и тоже наклоняет.

image

Lilium Jet — много отклоняемых винтов. Заявлена очень высокая скорость и дальность полета.

Хитрость в том, что надо безопасно отклонять роторы при взлете и посадке. Это непросто.

Толкающим воздушный винт (Pusher prop)


Если не отклонять роторы, можно летать, добавив дополнительный, более традиционный пропеллер, как правило, сзади. Это конструкция Kitty Hawk Cora, и ещё нескольких самолётов, в том числе грузового самолёта Elroy. Последний использует бензиновый двигатель для непосредственного управления толкающим винтом.

image

Испытательный полет Kitty Hawk «Cora» с 12 роторами для VTOL и винтом для полета с фиксированным крылом.

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

Отклоняемый фюзеляж (tilt-plane)


image

Opener «Blackfly» уже летает. Дизайн очень прост, 8 роторов.

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

Полностью электрический или гибрид


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

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

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

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

Большой, маленький или без колес


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

Прийти к компромиссу не так просто. Один большой вопрос — куда ставить все транспортные средства, если на них нельзя ездить. Многие конструкции, хоть они и не огромные, по-прежнему размером с 4 или 6 автомобилей. Если их миллион, то в городе не будет места, куда бы их можно было поставить. Даже если их 100 тысяч, это все равно много. Они могут передвигаться в открытых зонах, летать в зарядные порты, но не могут быть где-либо, где они не смогут приземлиться. Они могут взлетать и перемещаться на другие площадки, когда нужно подождать, пока расчищают аэродром, но не смогут ездить по дорогам.

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

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

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

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

image

Terrafugia — переходный вариант между традиционным самолетом с фиксированным крылом на бензине, только умеет складывать крыло для езды по дороге.

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

В настоящее время только очень немногие конструкции имеют колеса или их план.

Взлетно-посадочная полоса или же ее отсутствие


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

image

Pal-V автожир/автомобиль в «сложенном режиме» в гараже.

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

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

Открытые лопасти, защищенные лопасти, винт в кольцевом обтекателе


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

image

Bell Helicopter с шестью гигантскими наклонными винтами в кольцевых обтекателях.

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

Пилот с лицензией, оператор дрона или автономная работа


В конечном счете все надеются сделать полностью автономные такси. По действующему законодательству это сделать невозможно, и это все еще является проблемой. Некоторые машины сначала будут выпущены для эксплуатации только с лицензированным пилотом. Из-за веса батареи их трудно квалифицировать как «легкие спортивные самолеты», которые нуждаются в более простой лицензии пилота, их не назовешь и сверхлегкими, но есть предположения, что ФАА (Федеральная авиационная администрация США) может изменить допустимый вес, чтобы иметь дело с электромобилями на аккумуляторах, которых никогда ранее не ожидали. Если они это сделают, это позволит транспортным средствам сдвинуться с мертвой точки и попасть в руки пользователей до того, как автоматика будет готова к использованию или легальна.

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

Такси с большой вместимостью или личный автомобиль


Несколько лет назад анонсировали Uber Elevate — крупную инициативу, чтобы добиться от компаний разработки аэротакси, которыми Uber сможет управлять для перемещения людей между вертолетными портами. Uber Elevate уточнили, что это будут транспортные средства с 4 пассажирами и пилотом (пока нет автоматики) и в основном взлетающие с крыш зданий в плотно населённых городах, чтобы не загружать трафик еще больше. Это одно видение, которое привело к появлению многих дизайнов транспортных средств. Попытка сделать эти машины только на одних батареях тот еще челлендж. При этом многие другие сфокусировались на личном автомобиле, с 1-3 пассажирами и режимом автопилота. Они предназначены либо для частной собственности, либо для такси на небольшие расстояния.

Полет, модель, прототип или мечта


image

Hoversurf пример отличного ховербайка, минималистичный, на одного человека.

Пока существует почти 100 проектов, и еще больше находятся в закрытом режиме, все они на разных стадиях разработки. Только 3 принимают заказы или продают. Лишь немногие имеют полноразмерные летающие аппараты, способные путешествовать на любое расстояние. Ещё несколько имеют полномасштабные прототипы, но они совершали только вертикальный полёт. С другой стороны, некоторые из таких компаний, как Airbus, Boeing, Bell Helicopter и Embraer, обладают обширными ресурсами и авиационным опытом. Еще несколько компаний имеют летающие полномасштабные модели. Остальные из 81 компаний, перечисленные на TransportUp, обладают только конструкциями и компьютерными моделями. Первые автомобили, готовые для производства, являются более старыми конструкциями (как у Pal-V и Terrafugia), машины только с ротором (как у Volokopter и некоторых летающих мотоциклов) и вполне возможно Blackfly, утверждают, что он будет выставлен на продажу в этом году в диапазоне $70 тыс.

Кто победит


На данный момент ведется много споров на эту тему. Однако я считаю, что с большой вероятностью выиграют следующие конструкции:
  1. Мультикоптер и неподвижное крыло, обеспечивающее эффективность и дальность действия.
  2. Небольшие и умеющие в какой-то степени передвигаться по местам, где они будут ждать пассажиров, но не способные ездить на длинные расстояния по дорогам.
  3. С защищенными лопастями, чтобы приземляться в самых разных местах и быть более тихими.
  4. Рассчитанные на одного или двух пассажиров, чтобы оставаться маленькими и легкими.
  5. Наклоняющийся самолет или самолет с толкающим винтом, чтобы избежать движения сложных частей поворотного винта.
  6. Изначально нуждающиеся в пилоте, в идеале — в ограниченной лицензии пилота.

А пока, пусть расцветает тысяча конструкций.



image
О компании ИТЭЛМА
Мы большая компания-разработчик automotive компонентов. В компании трудится около 2500 сотрудников, в том числе 650 инженеров.

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

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


Читать еще полезные статьи:

Let's block ads! (Why?)

Гужевой транспорт 21 века


Недавно Грете Тунберг предложили осла в качестве самого экологичного транспорта… и тут возникает вопрос! А что если бы действительно шведская активистка выбрала бы гужевой транспорт? Какие варианты возможны сейчас?

Вариант №1


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

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


Сейчас «конки» уже практически не используются, но все еще остаются места где они сохранились.

В Австралии в городе Виктор-Харбор работает трамвай-конка. Эта линия выполняет функции перевозки туристов на остров с пингвинами, и проходит полностью по дамбе-мосту.

«Конка» Douglas Horse Tramway расположенная на острове Мен продолжает работать, несмотря на попытки ее закрыть. В отличие от австралийской линии ее используют и как обычный транспорт.

Кроме традиционного привода вагона-конки были и другие.

Например в начале был циклопед.

Идея была простая — соединить лошадь и беговую дорожку.

Конструкция создана британским математиком и изобретателем Томасом Шоу Брандретом в 1829 году. Вес был около 3 тонн, а максимальная скорость — 8—9,6 км в час.

Более совершенный привод был разработан в 1850 году итальянцем Клементе Массерано. Главной доработкой было наличие коробки передач, что позволило буксировать 30 вагонов даже при подъёме на склон при наличии всего 2 — 4 лошадей. КПП позволяло передвигать состав как в прямом, так и в обратном направлениях, и даже отключать передачу момента на колеса для остановки.

Практически достигнутая скорость — 11 км/ч, а в теории было возможно ускорить поезд до 24—33 км/ч.

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

Вариант №2


Движение по ж/д пути не единственный способ использования живой силы.

Так в наше время есть возможность использовать кареты похожие на те что были в 19 веке. Один из самых традиционных вариантов — кареты амишей в США.


Характеристики этого транспортного средства традиционные — одна лошадиная сила в натуральном эквиваленте.

Материалы современные, как и некоторые технологии…

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

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

Тормоза могут быть как барабанные, так и дисковые.

Электрика присутствует в минимальном количестве чтобы соответствовать правилам дорожного движения. В Огайо и Пенсильвании повозки по закону должны быть оснащены фонарями и 99% повозок имеют приборную доску, на которой установлено управление освещением. Есть передние фары, задние габариты и указатели поворотов.

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

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

Внутри «начинка» бывает разной. Могут присутствовать опции в виде отопления кабины пропаном, держателей для чашек и спидометрa (что при средней скорости от 5 до 8 миль выглядит странно).

Средняя стоимость повозки $8000. Срок службы 20 — 30 лет с возможностью капремонта (некоторые отремонтированные «тачки» ходят до 50 лет!).

В салоне может ехать в зависимости от конструкции от 5 до 6 пассажиров.

Управлять повозкой разрешено амишу любого возраста, и права на этот вид транспорта не требуются, как и номерные знаки (кроме штата Индиана где требуют установку номеров в обязательном порядке).

Неплохой способ вернутся в 19 век в 21-ом да?

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

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

Так еще в годы Депрессии (1929−1933) в результате проблем с топливом в США и Канаде многие автовладельцы переделывали свои автомобили в гужевые машины. В США их называли «повозки Гувера» (по имени тогдашнего президента страны), а в Канаде — «багги Беннетта» (в честь премьер министра того времени).

Для преобразования автомобиля в повозку снимали все «лишнее» (двигатель, кпп и т. д.).

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

После окончания голодных 30-х многие владельцы переделывали свои автомобили обратно на ДВС-тягу, но часть таких повозок так и остались на конной тяге (видимо это уже не имело практического смысла по причине износа «кареты»).

Идея автомобиль + лошадь не умерла и в наше время, и иногда получает продолжение…

В Ливии используют сочетание верблюды + джип.


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

В 2017 году итальянская дизайн-студия Camal нарисовала концепт гибридной кареты Viva.

Беспилотник-карета по задумке дизайнеров будет иметь 3 режима работы.

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

Характеристики Viva схожи с автомобильными.

Длина — 7 м; Ширина — 2.5 м; Высота — 2 м. Салон четырехместный. Максимальная скорость в безлошадном режиме по логике концепта может быть и автомобильной.

Есть у этого сочетания лошади и кареты и ряд очевидных недостатков.

1. Несмотря на наличие у животных «подгузников» некоторое количество отходов все же может попасть на дорогу.

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

Решение второй проблемы было найдено в 20 веке… надо было просто установить телегу впереди лошади!

История знает ряд примеров таких карет.

Например этот экипаж перевозил в 1905 году коньяк.

А этот только водителя и пассажира.

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

Вариант №3


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


Так в 2007 году в ОАЭ изобретатель Хади Мирхеджази (Hadi Mirhejazi) впервые создал конемобиль с беговой лентой внутри. Назвал он этот аппарат — Naturmobile.

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

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

Максимальная скорость этого аппарата планируется в рамках 60 — 80 км/ч, а достичь таких показателей должна помочь 20-скоростная коробка передач.

Правда похоже на импульсорию?

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

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

Лошадь достаточно крупный «мотор», и может не совсем удобный по габаритам. Поэтому возможно есть смысл применять не беговой дорожке и других животных. Изначально Грете предлагали ослов, однако в истории можно найти и другие варианты.
P.S. — Несмотря на то что гужевой транспорт считается анахронизмом, в новостях иногда попадаются сообщения об возвращении «живой тяги». Нашумевшее решение в Омской области не единственный пример.

Let's block ads! (Why?)