Posted on 12. December 2019

Знакомство с System.Threading.Channels

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

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

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

Что такое Канал?

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

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

Для начала нам нужен наш тип, к которому добавим несколько простых методов:

public sealed class Channel<T>

{

    public void Write(T value);

    public ValueTask<T> ReadAsync(CancellationToken cancellationToken = default);

}

 

Метод Write позволяет нам его использовать для вывода данных в канал, и ReadAsync метод дает нам потреблять от него. Так как решили, что наш канал неограничен, ввод данных в него всегда завершается успешно и синхронно, так же, как запрос Add в List<T> поэтому сделали его не асинхронным и возвращающим пустоту. В противоположность, наш метод использования это ReadAsync который является асинхронным, потому что данные, которые хотим использовать, могут быть еще не доступны, и, таким образом, нам нужно будет дождаться их обновлений.  И пока начинаем разработку, не слишком обеспокоены производительностью, также не хотим иметь много ненужных дополнительных расходов. Так как ожидаем, что будем читать, данные которые уже доступны для использования, метод ­­­­­ ReadAsync возвращает ValueTask<T> а не к Task<T>, так что можем сделать это без выделения при синхронном завершении.

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

private readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);

Используем ConcurrentQueue<T> для хранения данных, освобождая нас от необходимости делать собственную блокировку для защиты структуры данных буферизации, такую как ConcurrentQueue<T> она является потоко-безопасной для любого количества производителей/потребителей. И используем SempahoreSlim он помогает координировать между производителями и потребителей и уведомлять потребителей о поступлении дополнительных данных.

Наш метод Write прост. Ему нужно сохранить данные в очереди и увеличить счетчик SemaphoreSlim, “публикуя” его:

public void Write(T value)
{
    _queue.Enqueue(value); // store the data
    _semaphore.Release(); // notify any consumers that more data is available
}

 

Метод ReadAsync почти так же прост. Нужно подождать, пока данные будут доступны, а затем вынуть их.

public async ValueTask<T> ReadAsync(CancellationToken cancellationToken = default)
{
    await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); // wait
    bool gotOne = _queue.TryDequeue(out T item); // retrieve the data
    Debug.Assert(gotOne);
    return item;
}

 

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

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

Теперь, когда понимаем основы того, что предоставляет канал, можем перейти к рассмотрению реальных API-интерфейсов System.Threading.Channel.

 

Представляем System.Threading.Channels

Основные абстракции, представленные в библиотеке System.Threading.Channels, написаны так:

public abstract class ChannelWriter<T>
{
    public abstract bool TryWrite(T item);
    public virtual ValueTask WriteAsync(T item, CancellationToken cancellationToken = default);
    public abstract ValueTask WaitToWriteAsync(CancellationToken cancellationToken = default);
    public void Complete(Exception error);
    public virtual bool TryComplete(Exception error);
}

 

и читается:

public abstract class ChannelReader<T>
{
    public abstract bool TryRead(out T item);
    public virtual ValueTask<T> ReadAsync(CancellationToken cancellationToken = default)
    public abstract ValueTask WaitToReadAsync(CancellationToken cancellationToken = default);
    public virtual IAsyncEnumerable<T> ReadAllAsync([EnumeratorCancellation] CancellationToken cancellationToken = default);
    public virtual Task Completion { get; }
}

 

Только что завершил свою собственную разработку и внедрение простого канала, большая часть поверхности API должна быть знакома. ChannelWriter<T> обеспечивает метод TryWrite, который очень похож на наш метод записи; тем не менее, это абстрактно и метод проб, который возвращает Boolean учитывать тот факт, что некоторые реализации могут быть ограничены в количестве элементов они могут на нем храниться и если канал был заполнен так, что запись не могла завершиться синхронно, TryWrite потребуется вернуть false, чтобы указать, что запись была неудачной.  Тем не менее ChannelWriter<T> также обеспечивает метод WriteAsync; в таком случае, канал переполнен и запись должна ждать (часто упоминается как «обратное воздействие»), можно использовать WriteAsync, с производителем в ожидании результата WriteAsync его можно будет использовать, только когда пространство освободиться.

Конечно, бывают ситуации, когда код может не сразу создать значение; если полученные значения будут дорогим или если значение, представляет собой дорогой ресурс, (к примеру, это большой объект, который занял бы много памяти, или, может быть, он хранит кучу открытых файлов) в таких случаях, производитель работает быстрее, чем потребитель, производитель может захотеть отложить создание значения, пока не убедиться, что запись будет успешной. Для этого, есть соответствующий сценарий WaitToWriteAsync. Создатель может ожидать WaitToWriteAsync возвращение true, и только затем создатель выбирает значение TryWrite или WriteAsync на канал.

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

public async ValueTask WriteAsync(T item, CancellationToken cancellationToken)
{
    while (await WaitToWriteAsync(cancellationToken).ConfigureAwait(false))
        if (TryWrite(item))
            return;
 
    throw new ChannelCompletedException();
}

 

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

В этом примере также показано, почему WaitToWriteAsync возвращает ValueTask<bool> вместо ValueTask, а также ситуации за пределами полного буфера, в которых TryWrite может возвращать false. Каналы поддерживают концепцию завершения, когда производитель может сигнализировать потребителю, что больше не будет произведено никаких товаров, что позволяет потребителю прекратить попытки потреблять. Это делается с помощью методов Complete или TryComplete , ранее показанных на ChannelWriter<T> (Complete  реализован для вызова TryComplete  и throw, если он возвращает false). Но если один продюсер помечает канал как завершенный, другим продюсерам нужно знать, что они больше не могут писать в канал; в этом случае TryWrite возвращает false, WaitToWriteAsync также возвращает false, а WriteAsync генерирует исключение ChannelCompletedException.

Большинство членов ChannelReader<T>, вероятно, тоже говорят сами за себя.TryRead  попытается синхронно извлечь следующий элемент из канала. ReadAsync также извлекает следующий элемент из канала, но если элемент не может быть получен синхронно, он возвращает задачу. И WaitToReadAsync возвращает ValueTask<bool>, который служит уведомлением о доступе элемента. Как и в случае WriteAsync для ChannelWriter<T>, ReadAsync является виртуальным, с базовой реализацией, осуществляемой в терминах абстрактных TryRead  и WaitToReadAsync; это не точная реализация в базовом классе, но очень близка:

public async ValueTask<T> ReadAsync(CancellationToken cancellationToken)
{
    while (true)
    {
        if (!await WaitToReadAsync(cancellationToken).ConfigureAwait(false))
            throw new ChannelClosedException();
 
        if (TryRead(out T item))
            return item;
    }
}

 

Существует множество типичных шаблонов того, как использовать канал ChannelReader<T>. Если он представляет собой бесконечный поток значений, один из подходов состоит в том, чтобы просто сидеть в бесконечном цикле потребителя через ReadAsync:

while (true)
{
    T item = await channelReader.ReadAsync();
    Use(item);
}

 

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

while (await channelReader.WaitToReadAsync())
    while (channelReader.TryRead(out T item))
        Use(item);

 

Вместо этого внутреннее «while» могло бы быть простым «if», но наличие тесного внутреннего цикла позволяет экономному разработчику избегать небольших дополнительных издержек WaitToReadAsync, так что TryRead будет успешно использовать этот элемент. Фактически, это точный шаблон, используемый методом ReadAllAsync. ReadAllAsync был представлен в .NET Core 3.0 и возвращает IAsyncEnumerable<T>. Это позволяет всем данным быть прочитанными из канала, используя знакомые языковые конструкции:

await foreach (T item in channelReader.ReadAllAsync())
    Use(item);

 

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

public virtual async IAsyncEnumerable<T> ReadAllAsync(
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    while (await WaitToReadAsync(cancellationToken).ConfigureAwait(false))
        while (TryRead(out T item))
            yield return item;
}

 

Последняя часть ChannelReader это Completion. Возвращает Task, который будет выполнено после завершения чтения канала. Это означает, что писатель отметил, что канал завершен, и все данные были использованы.

Реализация встроенных каналов

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

Тип Channel<TWrite, TRead> предоставляет свойство Writer и свойство Reader, которое возвращает ChannelWriter<TWrite> и ChannelReader<TRead>  соответственно:

public abstract class Channel<TWrite, TRead>
{
    public ChannelReader<TRead> Reader { get;  }
    public ChannelWriter<TWrite> Writer { get; }
}

 

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

public abstract class Channel<T> : Channel<T, T> { }

 

Нетипичный Channel type обеспечивает фабрики для нескольких реализаций Channel<T>:

public static class Channel
{
    public static Channel<T> CreateUnbounded<T>();
    public static Channel<T> CreateUnbounded<T>(UnboundedChannelOptions options);
 
    public static Channel<T> CreateBounded<T>(int capacity);
    public static Channel<T> CreateBounded<T>(BoundedChannelOptions options);
}

 

Метод CreateUnbounded создает канал без ограничения на количество элементов, которые могут быть сохранены (конечно, в какой-то момент он может выйти за лимит памяти, так же, как с List<T> любые другие подборки), очень похоже на канальный тип, который реализовали в начале этого поста. TryWrite всегда возвращает true, и оба его WriteAsync и WaitToWriteAsync будут всегда завершаться синхронно.

Метод CreateBounded создает канал с явным ограничением. Как и в случае CreateUnbounded, TryWrite возвращает значение true, и WriteAsync и WaitToWriteAsync завершатся синхронно. Но как только канал заполняется, TryWrite возвращает false, и WriteAsync и WriteAsync завершаются асинхронно, выполняя свои возвращенные задачи только при наличии свободного места. (Само собой разумеется, что все эти API, которые принимают CancellationToken, также могут быть прерваны запросом отмены).

В CreateUnbounded, и CreateBounded есть перегрузки, которые принимает тип ChannelOption. Эта базовая опция предоставляет параметры, которые могут контролировать поведение любого канала. Например, он предоставляет свойства SingleWriter и SingleReader, которые позволяют создателю указывать ограничения, которые они готовы принять; разработчик устанавливает для SingleWriter значение true, чтобы указать, что один производитель будет одновременно обращаться к устройству записи, и аналогичным образом устанавливает для SingleReader значение true, чтобы указать, что не более одного потребителя будет одновременно обращаться к читателю. Это позволяет методам специализировать созданную реализацию, оптимизируя ее на основе предоставленных опций; например, если параметры, переданные CreateUnbounded, указывают на SingleReader как true, он возвращает реализацию, которая также позволяет избежать операций с блокировкой при чтении, что значительно снижает накладные расходы, связанные с потреблением из канала. Базовые ChannelOptions также предоставляют свойство AllowSynchronousContinuations. Как и в случае с SingleReader и SingleWriter, по умолчанию используется значение false, а для создателя значение true означает подписку на некоторые оптимизации, которые также имеют серьезные последствия для написания кода.  В частности, AllowSynchronousContinuations в некотором смысле позволяет производителю временно стать потребителем. Допустим, в канале нет данных, и потребитель приходит и вызывает ReadAsync. Ожидая задачу, возвращенную из ReadAsync, этот потребитель эффективно подключает обратный вызов, который будет вызываться при записи данных в канал. По умолчанию этот обратный вызов будет вызываться асинхронно, при этом производитель записывает данные в канал, а затем ставит в очередь вызов этого обратного вызова, что позволяет производителю одновременно идти своим путем, пока потребитель обрабатывается каким-то другим потоком. Однако в некоторых ситуациях для производительности может быть выгодно, чтобы этот производитель, записывающий данные, также сам обрабатывал обратный вызов, например, вместо того, чтобы TryWrite ставил в очередь вызов обратного вызова, он просто вызывает сам обратный вызов. Это может значительно сократить накладные расходы, но также требует глубокого понимания окружающей среды, так как, например, если вы удерживали блокировку при вызове TryWrite, а для параметра AllowSynchronousContinuations установлено значение true, вы можете в конечном итоге вызвать обратный вызов, удерживая блокировку, который (в зависимости от того, что пытался сделать обратный вызов) можете в конечном итоге наблюдать за некоторыми сломанными инвариантами, которые пытается поддерживать ваша блокировка.

BoundedChannelOptions передается слоям CreateBounded для дополнительных опций, связанных с ограничением. В дополнение к максимальной емкости, поддерживаемой каналом, он также предоставляет перечисление BoundedChannelFullMode, которое указывает, что записи поведения должны возникать при заполнении канала:

  public enum BoundedChannelFullMode
{
    Wait,
    DropNewest,
    DropOldest,
    DropWrite
}

 

TryWrite  на полном канале возвращает false, WriteAsync  вернет задачу, которая будет выполнена только тогда, когда освободится место, и запись может быть успешно завершена, и аналогично WaitToWriteAsync  будет завершена только тогда, когда пространство станет доступным. Вместо этого в трех других режимах запись всегда выполняется синхронно, удаляя элемент, если канал заполнен. DropOldest удалит «самый старый» элемент из очереди, что означает, что какой-либо элемент будет в дальнейшем удален потребителем. И наоборот, DropNewest удалит новый элемент, какой элемент был записан в канал в последний раз. И DropWrite удаляет элемент, который в данный момент пишется, то есть, например, TryWrite вернет true, но добавленный элемент будет немедленно удален.

Выполнение

С точки зрения API, представленные абстракции относительно просты, что является большой частью того, откуда исходит сила библиотеки. Простые абстракции и несколько конкретных реализаций, которые должны удовлетворять потребности разработчиков на 99,9%. Конечно, площадь поверхности библиотеки может указывать на простоту реализации. По правде говоря, в реализации есть приличная сложность, в основном сфокусированная на обеспечении высокой пропускной способности при одновременном использовании простых шаблонов потребления, легко используемых в потреблении кода. Реализация, например, делает все возможное, чтобы минимизировать распределение. Возможно, вы заметили, что многие методы возвращают ValueTask и ValueTask<T>, а не Task и Task<T>. Как увидели в нашем тривиальном примере реализации в начале этой статьи, мы можем использовать ValueTask<T>, чтобы избежать выделения при синхронном завершении методов, но реализация System.Threading.Channels также использует расширенные интерфейсы IValueTaskSource и IValueTaskSource<T>, чтобы избежать выделения ресурсов даже когда различные методы завершаются асинхронно и нужно возвращать задачи.

Рассмотрим этот тест:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Threading.Channels;
using System.Threading.Tasks;
 
[MemoryDiagnoser]
public class Program
{
    static void Main() => BenchmarkRunner.Run();
 
    private readonly Channel s_channel = Channel.CreateUnbounded();
 
    [Benchmark]
    public async Task WriteThenRead()
    {
        ChannelWriter writer = s_channel.Writer;
        ChannelReader reader = s_channel.Reader;
        for (int i = 0; i < 10_000_000; i++)
        {
            writer.TryWrite(i);
            await reader.ReadAsync();
        }
    }
}

 

Здесь мы просто тестируем пропускную способность и распределение памяти на неограниченном канале при записи элемента, а затем читаем этот элемент 10 миллионов раз, это означает, что элемент всегда будет доступен для чтения, и, таким образом, чтение всегда будет завершаться синхронно, что дает следующие результаты на моем компьютере (72 байта, показанные в столбце Allocated, предназначены для одной Задачи, возвращенной из WriteThenRead):

 

Method                                          Mean                            Error StdDev Gen 0    Gen 1    Gen 2    Allocated

WriteThenRead                           527.8 ms                    2.03 ms              1.90 ms                                                       72 B

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

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Threading.Channels;
using System.Threading.Tasks;
 
[MemoryDiagnoser]
public class Program
{
    static void Main() => BenchmarkRunner.Run();
 
    private readonly Channel s_channel = Channel.CreateUnbounded();
 
    [Benchmark]
    public async Task ReadThenWrite()
    {
        ChannelWriter writer = s_channel.Writer;
        ChannelReader reader = s_channel.Reader;
        for (int i = 0; i < 10_000_000; i++)
        {
            ValueTask vt = reader.ReadAsync();
            writer.TryWrite(i);
            await vt;
        }
    }
}

 

который на моей машине за 10 миллионов операций записи и чтения дает такие результаты:

Method                                          Mean                            Error StdDev Gen 0    Gen 1    Gen 2    Allocated

ReadThenWrite                           881.2 ms                    4.60 ms              4.30 ms                                                       72 B

 

Итак, есть некоторые дополнительные издержки, когда каждое чтение завершается асинхронно, но даже здесь мы видим нулевое распределение для 10 миллионов асинхронно завершающих чтений (опять же, 72 байта, показанные в столбце Allocated, предназначены для Задачи, возвращаемой из ReadThenWrite)!

Комбинаторы

Обычно потребление каналов является простым, используя один из подходов, показанных ранее. Но, как и в случае с IEnumerables, можно также выполнять различные виды операций по каналам для достижения определенной цели. Например, допустим, я хочу дождаться поступления первого элемента от одного из двух предоставленных читателей; Я мог бы написать что-то вроде этого:

public static async ValueTask> WhenAny(
    ChannelReader reader1, ChannelReader reader2)
{
    var cts = new CancellationTokenSource();
    Task t1 = reader1.WaitToReadAsync(cts.Token).AsTask();
    Task t2 = reader2.WaitToReadAsync(cts.Token).AsTask();
    Task completed = await Task.WhenAny(t1, t2);
    cts.Cancel();
    return completed == t1 ? reader1 : reader2;
}

 

Здесь мы просто запрашиваем WaitToReadAsync на обоих каналах и возвращаем считыватель в зависимости от того, что завершается первым. Одна из интересных вещей, которую стоит отметить в этом примере, это то, что в то время как ChannelReader<T> имеет много общего с IEnumerator<T>, этот пример не может быть хорошо реализован поверх IEnumerator<T> (или IAsyncEnumerator<T>).

I{Async}Enumerator<T> предоставляет метод MoveNext{Async}, который перемещает курсор вперед к следующему элементу, который затем открывается из Current. Если бы мы попытались реализовать такой WhenAny поверх IAsyncEnumerator<T>, нам нужно было бы вызвать MoveNextAsync для каждого. При этом мы потенциально могли бы перейти к следующему пункту. Если бы мы затем использовали этот метод в цикле, мы, скорее всего, в конечном итоге пропустили бы элементы из одного или обоих перечислителей, потому что мы потенциально могли бы расширить перечислитель, который мы не вернули из метода.

Отношение к остальной части .NET Core

System.Threading.Channels является частью общей платформы .NET Core, что означает, что приложение .NET Core может начать использовать его, не устанавливая ничего дополнительного. Он также доступен в виде отдельного пакета NuGet, хотя отдельная реализация не имеет всех оптимизаций, в значительной степени потому, что встроенная реализация может использовать преимущества дополнительной среды выполнения и поддержки библиотеки. NET Core.

Он также используется множеством других систем в .NET. Например, ASP.NET использует каналы как часть SignalR, а также в своей транспортировке Kestrel на основе Libuv. Каналы также используются будущей реализацией QUIC, которая в настоящее время разрабатывается для .NET 5.

Библиотека System.Threading.Channels также выглядит немного похожей на библиотеку System.Threading.Tasks.Dataflow, которая уже много лет доступна в .NET. В некотором смысле библиотека потоков данных является расширенным набором библиотеки каналов; в частности, тип BufferBlock<T> из библиотеки потока данных предоставляет большую часть той же функциональности. Однако библиотека потоков данных также ориентирована на другую модель программирования, в которой блоки связаны между собой так, что данные автоматически передаются от одного к другому. Он также включает расширенные функциональные возможности, которые поддерживают, например, форму двухфазной фиксации, с несколькими блоками, связанными с одними и теми же потребителями, и теми пользователями, которые позволяют извлекать механизмы из нескольких блоков без блокировки, которые необходимы для его включения, гораздо более сложны, и, в то же время, более мощные и более дорогие. Это очевидно, просто написав такой же тест для BufferBlock<T>, как мы делали ранее для каналов.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
 
[MemoryDiagnoser]
public class Program
{
    static void Main() => BenchmarkRunner.Run();
 
    private readonly Channel _channel = Channel.CreateUnbounded();
    private readonly BufferBlock _bufferBlock = new BufferBlock();
 
    [Benchmark]
    public async Task Channel_ReadThenWrite()
    {
        ChannelWriter writer = _channel.Writer;
        ChannelReader reader = _channel.Reader;
        for (int i = 0; i < 10_000_000; i++)
        {
            ValueTask vt = reader.ReadAsync();
            writer.TryWrite(i);
            await vt;
        }
    }
 
    [Benchmark]
    public async Task BufferBlock_ReadThenWrite()
    {
        for (int i = 0; i < 10_000_000; i++)
        {
            Task t = _bufferBlock.ReceiveAsync();
            _bufferBlock.Post(i);
            await t;
        }
    }
}

 

 

Method                                          Mean                            Error StdDev Gen 0    Gen 1    Gen 2    Allocated

Channel_ReadThenWrite       878.9 ms                    0.68 ms              0.60 ms                72 B                                 72 B

BufferBlock_ReadThenWrite                                  20,116.4 ms         192.82 ms           180.37 ms           1184000.0000    2000.0000                                                                                        7360000232 B

 

Это не означает, что библиотека System.Threading.Tasks.Dataflow не должна использоваться. Он позволяет разработчикам кратко выражать большое количество концепций и может демонстрировать очень хорошую производительность применительно к задачам, которые ему наиболее подходят. Однако, когда все, что вам нужно, - это структура передачи данных между одним или несколькими производителями и одним или несколькими потребителями, которую вы внедрили вручную, System.Threading.Channels - гораздо более компактная и экономная ставка.

Что же дальше?

Надеюсь, на данный момент вы более детально разобрались в библиотеке System.Threading.Channels и сможете с ее помощью улучшить ваши приложения. Попробуйте, и будем рады вашим отзывам, предложениям, проблемам и PR, чтобы улучшить ее

https://github.com/dotnet/runtime.

Источник

 



Posted on 15. November 2019

Анонс Windows Community Toolkit v6.0

Microsoft объявляет о следующем обновлении Windows Community Toolkit версии 6.0, которое стало возможным, благодаря помощи и вкладу нашего сообщества разработчиков. В этом выпуске реализована поддержка ARM64 для инструментария, а также обновление XAML Islands для поддержки .NET Core 3. Кроме того, у нас есть новые функции, такие как элемент управления "Eye Dropper " и новые помощники уведомлений Win32. У нас также есть обновление Microsoft Graph благодаря XAML контролам.


Смотрите более подробную информацию об этих функциях ниже.

XAML Islands приближает UWP к WPF, WinForms и Win32

XAML  Islands позволяет разработчику улучшить внешний вид и функциональность существующего приложения Win32 для WPF, Windows Forms или C ++, использовать новейшие функции пользовательского интерфейса Windows 10, которые доступны только через UWP контроллы, например, рукописный ввод:

 

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

 

Документация для XAML Islands.

 

Поддержка ARM64

Windows Community Toolkit теперь поддерживает приложения, которые ориентированные на ARM64. Что позволяет разработчикам использовать преимущества времени автономной работы и производительности, работая на собственной архитектуре для таких устройств, как Surface Pro X. Разработчики также тесно сотрудничали с командой Win2D, чтобы убедиться, что она поддерживает ARM64. Это было важно для Lottie и других функций инструментария, основанных на Win2D.

Улучшенная анимация Lottie

Обновление предоставляет больше возможностей Adobe After Effects для Lottie-Windows, в том числе Linear и Radial Gradients, Masks, Track Mattes поддержку codegen для Image Layers . Мы надеемся, что эти дополнения позволят дизайнерам и разработчикам приложений создавать еще более привлекательные визуальные интерфейсы для пользователей Windows 10. Поскольку некоторые из этих функций основаны на более новых SDK, Lottie-Windows теперь также предлагает адаптивное управление версиями. Мы рассчитываем на то, что сообщество определит приоритеты работы с функциями, поэтому, пожалуйста, продолжайте оставлять свои ценные отзывы и предложения для Lottie-Windows!

 

Eye Dropper

Новый элемент управления “Eye Dropper” позволяет вам легко и быстро выбирать цвет приложения.

Документация для EyeDropper.

 

Превью XAML Graph Controls

Новое дополнение к Windows Community Toolkit позволяет разработчикам легко проходить проверку подлинности и получать доступ к Microsoft Graph в приложениях Windows 10 для создания обширных данных. Эти элементы управления доступны в качестве предварительного просмотра версии 6.1 и будут работать с приложениями UWP и в приложениях WPF / WinForms для Win32 через XAML Islands в .NET Core 3. Кроме того, совсем скоро с помощью Xamarin и Uno Platform у вас появится возможность использовать их на Android и iOS.

 

Читайте об этих новых элементах управления в нашем официальном заявлении или на GitHub.

Начни сегодня

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

Напоминаем, что вы можете начать работу, следуя нашему учебному пособию docs.microsoft.com tutorial, или просмотреть последние функции, установив образец приложения Windows Community Toolkit из Магазина Microsoft. Если вы хотите внести свой вклад в разработку, пожалуйста, присоединяйтесь на GitHub! Чтобы присоединиться к беседе в Twitter, используйте хештег #WindowsToolkit.

Всем удачного кодирования!

Источник



Posted on 1. November 2019

Windows работает над совершенствованием робототехники

Робототехника развивается быстро. Многое произошло с тех пор, как Microsoft анонсировала экспериментальный выпуск Robot Operating System (ROS ™) [1] для Windows, на прошлогодней выставке ROSCON в Мадриде. Поддержка ROS стала общедоступной в мае 2019 года, что позволило роботам воспользоваться преимуществами всемирной экосистемы Windows. Которая предоставляет собой - обширную платформу устройств, набора инструментов для разработчиков мирового класса, интегрированную безопасность, долгосрочную поддержку и глобальную партнерскую сеть. Кроме того, мы предоставили доступ к расширенным функциям Windows, таким как Windows Machine Learning и Vision Skills, и предоставили возможность подключения к облачным службам Microsoft Azure IoT.

 

В этом году на мероприятии ROSCON в Макао мы анонсировали, что продолжаем увеличивать наши возможности ROS с поддержкой ROS / ROS2, расширением Visual Studio Code для ROS и поддержкой шаблонов ROS Azure VM для тестирования и моделирования. Это позволяет разработчикам проще и быстрее создавать решения ROS, чтобы идти в ногу с современными технологиями и потребностями клиентов. Мы с нетерпением ждем возможности добавления роботов на 900 миллионов устройств, которые будут работать под управлением Windows 10 во всем мире.

Расширение Visual Studio Code для ROS

В июле Microsoft опубликовала предварительную версию расширения VS Code для ROS. С тех пор мы расширяем его функциональные возможности - добавляем поддержку Windows, отладку и визуализацию, чтобы упростить разработку решений для ROS. Расширение поддерживает:

- Автоматическая настройка среды для разработки ROS

- Запуск, остановка и мониторинг состояния времени выполнения ROS

- Автоматическое обнаружение задач сборки

- Создание ROS-пакета в один клик

- Горячие клавиши rosrun для и roslaunch

- Разработка Linux ROS

 

Кроме того, расширение добавляет поддержку для отладки узла ROS, используя расширения C ++ и Python. В настоящее время, разработчики, в VS Code, могут создать конфигурацию отладки для ROS, чтобы присоединиться к узлу ROS. Расширение теперь поддерживает отладку узлов ROS, запускаемых из roslaunch при запуске ROS.

Расширение Visual Studio Code для ROS, показывающее состояние ядра ROS и отладку для roslaunch.

Unified Robot Description Format (URDF)  - это формат XML для представления модели робота, а Xacro - это макроязык XML для упрощения файлов URDF. Расширение включает поддержку предварительного просмотра файла URDF / Xacro с использованием Robot Web Tools, что помогает разработчикам ROS легко вносить изменения и мгновенно наглядно представлять изменения в VS Code.

 

Расширение Visual Studio Code для ROS с предварительным просмотром URDF.

 

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

 

ROS на Windows VM шаблон в Azure

С переходом на облако многие разработчики приняли гибкие методы разработки. Они часто хотят развернуть свои приложения в облаке для тестирования и моделирования сценариев, когда их разработка завершена. Они выполняют итерацию быстро и многократно развертывают свои решения в облаке. Шаблон Azure Resource Manager - это файл JavaScript Object Notation (JSON), который определяет инфраструктуру и конфигурацию проекта. Чтобы упростить процесс тестирования и развертывания в облаке, мы публикуем ROS на Windows шаблон VM, который создаст VM Windows и установит последний билд в VM с использованием расширения CustomScript. Вы можете попробовать, кликнув по ссылке.

 

Поддержка расширения ROS и ROS2

Microsoft расширяет поддержку ROS и ROS2, включая создание поддерживаемых Microsoft узлов ROS, сборку и предоставление пакетов Chocolatey для следующих релизов ROS (Noetic Ninjemys) и ROS2 (Eloquent Elusor).

 

Драйвер Azure Kinect ROS

Внутреннее строение Azure Kinect.

 

Azure Kinect Developer Kit - это новейший датчик Kinect от Microsoft. Azure Kinect содержит тот же датчик глубины, который используется в Hololens 2, а также камеру 4K, аппаратно-синхронизированный акселерометр, гироскоп (IMU) и микрофон состоящий из 7 элементов. Наряду с выпуском аппаратного обеспечения Microsoft предоставила узел ROS для управления Azure Kinect и вскоре будет поддерживать ROS2.

Azure Kinect ROS Node излучает поток PointCloud2, который включает обширную информацию о цвете, наравне с глубиной изображения, необработанных данных с камер IR и RGB и высокоскоростных данных IMU.

Цветной вывод Pointcloud Azure Kinect в инструменте rViz.

 

Сообщество способствовало возможности отслеживание тела! Это ссылка к Azure Kinect Body Tracking SDK, выводит маски изображений каждого отслеживаемого человека и позы тела в качестве маркеров служат движений суставов индивида.

Пример отслеживания скелета в rViz.

Вы можете заказать Azure Kinect DK в Магазине Майкрософт, а затем начать использовать узел Azure Kinect ROS.

 

Отслеживание Windows ML в ROS Node

Windows Machine Learning API позволяют разработчикам использовать предварительно обученные модели, в своих приложениях на устройствах с Windows 10. Это предоставляет разработчикам несколько преимуществ:

- сокращение времени ожидания, результаты в режиме онлайн: Windows может выполнять задачи оценки ИИ, используя возможности локальной обработки ПК с аппаратным ускорением, используя любой графический процессор DirectX 12. Это позволяет в реальном времени анализировать большие локальные данные, такие как изображения и видео. Предоставленные результаты могут эффективно использоваться при активной рабочей нагрузке, например, игровые движки, или фоновые задачи или такие как индексация для поиска.

- Снижение эксплуатационных расходов. Совместно с платформой Microsoft Cloud ИИ разработчики могут создавать доступные комплексные решения для искусственного интеллекта, которые объединяют модели обучения в Azure с развертыванием на устройствах Windows для оценки. Значительная экономия может быть достигнута за счет сокращения или исключения затрат, связанных с пропускной способностью, из-за использования больших наборов данных, таких как отснятый материал камеры или сенсорная телеметрия. Многоплановый объём работ может обрабатываться в режиме реального времени на периферии с минимальными выборочными данными, которые будут отправляться в облако для улучшения обучения на наблюдениях.

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

 

Узел Windows Machine Learning ROS позволит ускорить вывод ваших моделей, публикуя маркер визуализации относительно рамки издателя изображений. Вывод Windows ML можно использовать для устранения препятствий или манипуляций.

 

Пример вывода модели с Windows ML. Модель используется с разрешения: www.thingiverse.com/thing:1911808.

 

Azure IoT Hub ROS Node

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

Azure IoT Hub ROS Node позволяет передавать сообщения ROS через концентратор IoT Azure. Эти сообщения могут быть обработаны с помощью функции Azure, переданы в хранилище BLOB-объектов или обработаны с помощью потоковой аналитики Azure для обнаружения неточностей. Кроме того, Azure IoT Hub ROS Node позволяет изменять свойства ROS Parameter на сервере с помощью динамической перенастройки, которая установлена на Azure IoT Hub Device Twin.

Узнайте больше и ознакомьтесь с некоторыми из этих технологий в действии на выставке ROSCON 2019 в Макао. Вы можете начать работу с ROS в Windows, кликнув по ссылке.

 

[1] ROS является торговой маркой Open Robotics.

Источник



Posted on 17. October 2019

Windows 10, версия 1909 что нового для разработчиков?

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

Новый Windows SDK не будет выпущен вместе с этой версией Windows, поскольку в этом релизе не представлены новые API. Это означает, что нет необходимости ориентироваться на Windows 10 версии 1909 или изменять файлы проекта.

Так как обновления SDK пока что нету, вы можете продолжить работу с Windows 10 версии 1903. Это можно сделать самым простым способом, установить Visual Studio 2019.

 

Новое с Windows 10, версия 1903

Центр разработки Windows имеет полный список того, что доступно для разработчиков, которые используют версию 1903. С тех пор мы презентовали Windows UI Library 2.2.

 

WinUI 2.2 выпущен в августе. WinUI имеет открытый исходный код, и каждый может ознакомиться с репозиторием WinUI GitHub, чтобы решить проблемы с файлами, обсудить новые функции и даже внести код. В WinUI 2.2 мы добавили новый элемент управления Tab View. Помимо введения новых обновлений Visual Style, был также апдейт для Navigation View. Мы всем рекомендуем использовать WinUI в своих приложениях UWP - это лучший способ получить новейшую систему проектирования Fluent, контроллы и иметь обратную совместимость с Windows 10 Anniversary Update.

 

2 простых шага для обновления вашей среды разработки

Если вы хотите обновить свою систему Windows 10 до версии 1909, вы можете загрузить ее по подписке VSS, либо воспользоваться WIP (Windows Insider Program) Release Preview Ring. У команды Insider есть отличный пост в блоге, в котором вы узнаете, как попасть в Release Preview Ring. Затем, просто зайдите в Visual Studio 2019 и установите последнюю версию SDK. В последней версии Visual Studio Windows 10 SDK (10.0.18362) уже выбран по умолчанию.

1.      Запустите установщик Visual Studio или перейдите по адресу https://www.visualstudio.com/downloads/ и загрузите его.

2.      Выберите «Universal Windows Platform development» в разделе Workloads, Windows 10 SDK (10.0.18362) будет включен по умолчанию

3.       Нажмите «Install»

 

 

Источник



Posted on 17. April 2019

Windows UI Library 2.2

WinUI 2.2 - это последний официальный выпуск Windows UI Library.

Вы можете добавить пакеты WinUI в свое приложение с помощью диспетчера пакетов NuGet: для получения дополнительной информации см. Начало работы с Windows UI Library.

 

WinUI - это проект с открытым исходным кодом, размещенный на GitHub. Мы приветствуем ваши сообщения об ошибках, предложения по улучшению кода в репозитории Windows UI Library.

 

История версий Microsoft.UI.Xaml 2.2

 

Официальный релиз Windows UI Library 2.2

Август 2019

 

Страница выпуска GitHub

Скачать пакет NuGet

 

Новые возможности

 

1)     TabView

Описание:

 

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

 

Документация:

https://docs.microsoft.com/en-us/uwp/api/microsoft.ui.xaml.controls.tabview?view=winui-2.2

 

2)     Обновления NavigationView

 

а) Обновление кнопки “Назад” NavigationView

 

Описание:

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

б) NavigationView - нет автоматического заполнения

 

Описание:

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

Документация:

https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/navigationview#top-whitespace

3)   Обновление Visual Style

а) Corner Radius

Описание:

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

GitHub Ссылка:

https://github.com/microsoft/microsoft-ui-xaml/issues/524

б) Border Thickness

 

Описание:

Свойство BorderThickness стало проще настраивать. Стандартные элементы управления были обновлены, чтобы уменьшить контуры, для более чистого и привычного вида

GitHub Spec Ссылка:

https://github.com/microsoft/microsoft-ui-xaml/issues/835

в) кнопка Visual

Описание:

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

GitHub Spec Ссылка:

https://github.com/microsoft/microsoft-ui-xaml/issues/953

г) SplitButton

Описание:

Визуальное отображение SplitButton по умолчанию было обновлено, чтобы сделать его более отчетливым от DropDownButton.

GitHub Spec Ссылка:

https://github.com/microsoft/microsoft-ui-xaml/issues/986

д) ToggleSwitch

Описание:

Ширина ToggleSwitch по умолчанию была уменьшена с 44 до 40 пикселей, поэтому он сбалансирован визуально, сохраняя удобство использования.

GitHub Spec Ссылка:

https://github.com/microsoft/microsoft-ui-xaml/issues/836

 

е) CheckBox и RadioButton

Описание:

Визуальные элементы CheckBox и RadioButton были обновлены, чтобы соответствовать остальной части изменений визуального стиля.

https://github.com/microsoft/microsoft-ui-xaml/issues/839

 

Предварительный релиз Microsoft.UI.Xaml 2.2.190702001

Июль 2019

Страница релиза GitHub

Скачать NuGetпакет загрузки

Экспериментальная особенность

·         TabView

Пререлиз Microsoft.UI.Xaml 2.2.20190416001

Апрель 2019

Страница релиза GitHub

Скачать NuGet пакет загрузки

Экспериментальные особенности

·         FlowLayout

·         LayoutPanel

·         RadioButtons

·         ScrollViewer

 

Источник



Posted on 16. January 2019

Выпущены WebRTC UWP и образец callstats.io интеграции

В прошлом году Microsoft объявили о поддержке связи в режиме реального времени на универсальной Windows платформе, которая разработана на развилке Google WebRTC.org репозитория. Проект позволяет UWP разработчикам создавать совместимые с Chrome и похожие по функциям RTC приложения для всех Windows 10 платформ, включая настольные ПК, ноутбуки с HoloLens, Xbox и ARM поддержкой. Интерес общества к проекту увеличивается, о чем говорит 26 тысяч NuGet загрузок и сотни писем от разработчиков на GitHub и на почте. WebRTC и ORTC UWP были значительно улучшены, и Microsoft сообщили о своем вкладе в UWP поддержку репозитория Google WebRTC.org – более подробная информация будет опубликована в ближайшие месяцы.

Однако, постоянно поступает просьба от разработчиков, которые хотят лучше понять качество WebRTC вызовов для своих приложений. Это может быть звонок из HoloLens в десктопный браузер или Xbox приложение потокового видео в реальном времени. С этой целью Microsoft сотрудничает с callstats.io, чтобы обеспечить портативную .NET интеграцию платформы для WebRTC сервисов мониторинга и статистики!

Сегодня Microsoft объявили о выпуске NuGet .NET Standard 2.0 пакета для callstats.io, наряду с образцом кода на основе исходного кода PeerCC UWP. В качестве .NET Standard библиотеки, он доступен для всех поддерживаемых .NET реализаций, включая .NET Framework, .NET Core, UWP, Xamarin и Unity платформы (ознакомьтесь с документацией по требованиям к версии). Образец PeerConnection представляет собой UWP приложение, которое устанавливает двухсторонний видеовызов и отправляет WebRTC статистику в callstats.io службу с 10-секундными интервалами.

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

Kognitiv Spark, лидер в области систем со смешанной реальностью и Windows 10 разработчик, использует callstats.io сервис со своим RemoteSpark приложением.

«Наша операционная команда столкнулась с вопросами, помогающими клиентам решить трудности с сетью, которые могут нарушить сеанс смешанной реальности», – говорит Райан Грум, соучредитель и технический директор Kognitiv Spark. «Благодаря встроенной поддержке Windows 10 мы смогли за несколько часов запустить callstats.io службу и начать использовать полные данные, доступные на ее панели, для обнаружения и решения проблем».

Ознакомьтесь с шагами для интеграции.

1) Создать учетную запись callstats.io.

  • Перейдите по ссылке https://dashboard.callstats.io/register и создайте Организацию.
  • Перейдите на вкладку приложений, чтобы создать приложение, и ввести SDK как другой «UWP».
  • Нажмите на левой боковой панели «Настройки приложения» и перейти на вкладку «Безопасность».
  • Нажмите «Новые учетные данные» и выберите ярлык ключа.
  • Выберите тип ключа как «ECDSA» и введите открытый ключ.
  • Под ключевыми деталями, нажмите на «вид» для учетных данных.
2) Для аутентификации с помощью callstats.io необходимо сгенерировать криптографические ключи и загрузить открытый ключ в сервис. С инструкциями в файле readme.md можно ознакомиться здесь. После выполнения этого шага в проекте должны получиться Config класс и .p12 сертификат. Это все, что необходимо для аутентификации в API REST callstats.io.

3) Перейдите по ссылке на NuGet Org.WebRtc.Callstats пакет, доступный в Вашем проекте здесь.

4) Если используется образец PeerCC, то можно найти классы для сбора данных из PeerCC и WebRTC в Stats папке, также можно использовать этот код в качестве примера для своей реализации. Добавлять файлы из папки PeerCC-Sample репозитория в проект нужно следующим образом:
  • PeerConnectionControllerStateChange.cs: отслеживает RTCPeerConnection изменения состояния и собирает данные о состояниях одноранговых соединений для отправки в callstats.
  • StatsController.cs: собирает данные из всего PeerCC семпла и использует Org.WebRtc.Callstats библиотеку для отправки подготовленных данных в REST API callstats.io.
  • WebRtcStats.cs: подготавливает статистику, полученную из RTCPeerConnection, для связи с callstats.io через REST API.
5) Инициализируйте callstats.io сервис и отправьте SDP и ошибки приложения через StatsController. Для примера, см. Conductor.cs в PeerCC-Sample.

6) Обработайте изменения ICE состояния в RTCPeerConnection и используйте PeerConnectionStateChange для отправки данных на сервис:

_peerConnection.OnIceGatheringStateChange += async() => 
 
            { 
 
                await PeerConnectionStateChange.StatsOnIceGatheringStateChange(_peerConnection); 
 
                Debug.WriteLine("Conductor: Ice connection state change, gathering-state=" + _peerConnection.IceGatheringState.ToString().ToLower()); 
 
            }; 
 
            _peerConnection.OnIceConnectionStateChange += async () => 
 
            { 
 
                if (_peerConnection != null) 
 
                    await PeerConnectionStateChange.StatsOnIceConnectionStateChange(_peerConnection); 
 
                else
 
                { 
                    await PeerConnectionStateChange.PeerConnectionClosedStateChange(); 
 
                    ClosePeerConnection(); 
 
                } 
 
};
7) Отправьте события уровня приложения для отключения и включения звука в StatsController. Для примера см. MainViewModel.cs в PeerCC-Sample.

Теперь образец должен быть полностью подключен к callstats.io сервису и готов к тестированию. Microsoft только начинает улучшать качество вызовов. В дальнейшем работа будет сфокусирована на усовершенствовании взаимодействия с пользователями с помощью дополнительных метрик и AI. Отправляйте Ваши фидбеки об использовании библиотеки, а также отзывы и предложения на GitHub или на email.

 



Posted on 3. May 2018

Новое имя UWP Community Toolkit: Windows Community Toolkit

Уже в следующем обновлении UWP Community Toolkit будет переименован на Windows Community Toolkit. Это действительно большой шаг в развитии инструментария и сообщества.

Впервые инструментарий был выпущен более полутора лет назад с 26 различными функциями. С тех пор Microsoft добавили пять новых пакетов в девять новых выпусков, каждый из которых включает в себя новые элементы управления, помощники, службы, расширения, объектные модели и многое другое - большинство из элементов поступает сразу из сообщества. Сегодня существует более 100 различных функций. Вы можете сравнить количество элементов управления (и категорий) на примере приложения с первоначальной версии:

UWP Community Toolkit Sample App (версия 1.0)

UWP Community Toolkit Sample App (версия 2.0)

Когда Microsoft выпустили UWP Community Toolkit, было много отзывов о добавление возможности для разработчиков, поделиться компонентами инструментария с другими инфраструктурами, такими как WPF, WinForms, Xamarin, .NET Core и другими. С помощью сообщества, в версии 2.0 Microsoft идентифицировали компоненты, которые могли быть учтены в библиотеках .NET Standard, и создали новые пакеты .NET Standard для того, чтобы все разработчики могли воспользоваться преимуществами работы сообщества. Сегодня многие из сервисов, помощников и парсеров являются кросс-платформенными и могут использоваться везде. Более того, Microsoft продолжает работать над тем, чтобы добавить еще больше преимуществ.

 

Главная цель сообщества - привлечение большего количества разработчиков, поэтому, начиная со следующей версии Windows Community Toolkit, Microsoft предоставят возможность другим Windows разработчикам, работающим на Windows 10 платформе, использовать все преимущества компонентов инструментария. Таким образом, новое имя привлечет всех Windows разработчиков.

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

Чтобы присоединиться к беседе в Twitter, используйте хэштег #windowstoolkit. Microsoft также будут рады видеть Вас на конференции Microsoft Build 2018!



Posted on 28. July 2017

Windows Store: трейлеры, улучшенные списки, скидки, и другие возможности

Во время Build 2017, Windows Store команда объявила несколько новых функций. Сегодня мы рассотрим этих возможности. 

 

  • Больше способов продвижения приложений и привлечения пользователей
  1. Создание более привлекательных страниц приложений с помощью видео трейлеров
  2. Быстрое создание и обновление страниц приложений с помощью импорта / экспорта
  • Дополнительные способы управления ценами, скидками и продажами
  1. Установка точной даты и времени, когда Ваше приложение должно быть доступно в Store
  2. Более точный контроль Ваших цен и их изменения
  3. Новые более гибкие варианты ценообразования 
  4. Просмотр всех возможных уровней цен в Excel
  • Эффективное отлаживание Ваших приложений с помощью CAB-файлов
  • Dev Center с современными и улучшенными панелями инструментов

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

Дополнительные способы продвижения приложений и привлечения пользователей

Видео-трейлеры - один из лучших способов привлечения клиентов. Теперь Вы можете загружать до 15 трейлеров на страницы Ваших приложений. При использовании трейлеров обязательно включите изображение с разрешением 1920 x 1080 пикселей (16: 9) в разделе рекламных изображений, которое появляется после прекращения воспроизведения видео. 

Видео трейлеры в Store - см. их здесь на ПК с Windows 10


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

Экспорт и импорт - Страница заполнения приложения для публикации

 

Больше способов управления ценами и продажами

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

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

Доступность расписания - страница ценообразования и доступности


 

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

Планирование изменения цены - страница ценообразования и доступности


 

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

Скидки управляют товарами, покупайте и попробуйте!

Настройка продаж - Страница ценообразования и доступности


 

Как понять цены на разных рынках? Теперь Вы можете просматривать все возможные уровни цен в Excel. Перейдите на страницу «Цены и доступность», выберите таблицу просмотра, и вы можете просматривать и экспортировать таблицу в CSV.

Просмотр всех ценовых уровней - Страница ценообразования и доступности


Использование современных и эффективных панелей инструментов в обновленном Dev Center 

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

Редизайн в Dev Center


Отлаживайте Ваши приложения более эффективно с помощью CAB-файлов

Команда Microsoft получила много отзывов о доступе к файлам CAB, с помощью которых можно отлаживать приложения, а также улучшать качество и производительность Ваших приложений и игр. Отчет о работоспособности позволяет определить, какие конфигурации версий ОС и приложений генерируют большинство сбоев, а также предоставляет ссылки на детали сбоя с отдельными CAB-файлами. Эти CAB файлы доступны только для пользователей, выполняющих любые Windows Insider flights of Windows 10 (медленные или быстрые), поэтому не все отказы будут включать в себя вариант загрузки CAB.

 

 

Вовлечение этих изменений при использовании Windows Store submission API

Если Вы используете Windows Store submission API для управления Вашими приложениями и играми, обратите внимание на следующие пункты:

 

  • Если вы управляете ценами с помощью Submission API, Вам нужно использовать новые уровни цен. Для этого вручную обновите приложение или игру, чтобы Вы могли просматривать новые уровни цен, принимать их и затем обновлять свой API код отправки, чтобы использовать эти новые значения уровня цен, которые можно найти в ценовой таблице по ценообразованию, и также на странице доступности в Dev Center, как описано выше.
  • Windows Store submission API не поддерживает все новые листинговые возможности в Store. Вы можете добавлять новые активы с помощью панели инструментов в Dev Center, а API-интерфейс отправки будет обновлен позднее в июле, чтобы Вы могли управлять этими новыми активами с помощью API. Более подробная информация о будущих возможностях API, включая трейлеры и параметры игр, указана в этой статье.
  • Если Вы используете модуль StoreBroker PowerShell для упрощения использования Windows Store submission API, Вы можете продолжать использовать его для управления теми же типами листинговых активов, которыми управляете сегодня. Однако Вы не сможете загружать новые типы активов с помощью StoreBroker, пока команда StoreBroker не опубликует обновление (примерно через несколько недель).

 

Прочитайте пост в этом блоге, чтобы узнать обо всех недавно добавленных функциях в Store и попробовать все доступные сегодня возможности. Если у Вас возникли трудности с поиском или использованием этих функций, обязательно сообщите, используя ссылку обратной связи в Dev Center (вверху справа от панели инструментов).



Posted on 2. July 2017

Наборы инструментов!

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

Сегодня поговорим о двух инструментах с открытым исходным кодом:

 

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

UWP Community Toolkit

UWP Community Toolkit - это сотрудничество между Microsoft и сообществом разработчиков UWP. Благодаря множеству функций, таких как вспомогательные функции, компоненты пользовательского интерфейса, анимации и сервисы приложения, UWP Community Toolkit - отличная экономия времени и гарантированный успех вашего приложения на новом уровне. 


В настоящее время набор инструментов имеет 12 выпусков и находится на v 1.4 (выпущен 3 апреля 2017 года). Он имеет более 80 участников, с тысячами коммитов, и данное сообщество постоянно работает над улучшением. Удобство заключается в том, что он разбит на несколько пакетов nuget, чтобы мы могли выбирать необходимое. 

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


Пример получения временной шкалы пользователя Twitter:

TwitterService.Instance.Initialize("consumer-key", "consumer-secret", "callback-uri");
ListView.ItemsSource = await TwitterService.Instance.GetUserTimeLineAsync("user-screenname", 50);
Можно найти полное демо-приложение в данном исходном коде или здесь, в Windows Store. Перейдите сюда, чтобы просмотреть полный список доступных функций (элементы управления, помощники и т. д.), и перейдите сюда для просмотра документации.

Telerik UI для UWP

Telerik UI для UWP от Progress Software - это недавно созданный инструментарий с открытым исходным кодом, содержащий удивительный набор средств управления бизнес-процессами (LOB), с помощью которых вы можете создавать собственные, бизнес-ориентированные UWP приложения. Благодаря таким элементам управления, как DataGrid и RadListView, Telerik UI предоставляет мощную сортировку, группировку и редактирование, которые можно ожидать от настольного приложения, а также замечательные возможности визуализации данных с такими элементами управления, как диаграммы, датчики и BulletGraphs.

Microsoft рекомендует посмотреть приложение Customer Database Example (примера базы данных клиентов) здесь, на GitHub, чтобы ознакомиться с DataGrid в действии, а также приложение SDK Examples. Здесь вы можете увидеть полный список доступных элементов управления и найти документацию (если вам необходима небольшая дополнительная помощь, Progress Software также предлагает профессиональную поддержку в пакете премиум-класса).

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

Вы можете установить UI для UWP в своем приложении, используя пакет nuget или же напрямую из источника. Если вы хотите узнать больше о том, почему Progress Software открывает исходный код Telerik для UWP, рекомендуем вам ознакомиться с этой увлекательной статьей.

Сотрудничество 

Если вы являетесь разработчиком, которому нравится участвовать в репозиториях GitHub и вносить свой вклад в сообщество, или если у вас есть идеи, как сделать что-то лучше для других разработчиков, оба набора инструментальных средств принимают запросы на загрузку, и каждый из них имеет свои собственные рекомендации по вкладам (здесь для UWP Community Toolkit, и здесь для Telerik UI для UWP).

В Завершении

Оба инструментария дополняют друг друга. Вы можете использовать их взаимодействующими в вашем приложении, чтобы предоставить пользователю восхитительный и значительный пользовательский опыт в вашем UWP приложении. Благодаря десяткам элементов управления пользовательского интерфейса, помощников, сервисов и т. д., вы можете публиковать приложения намного быстрее и с точной уверенностью в их продуктивности. Мы с нетерпением ждем вашего инструментария UWP Community Toolkit и UI для UWP приложений в Windows Store!


Posted on 26. June 2017

Анимации в Visual Layer с Windows 10 Creators Update

Windows 10 Creators Update является третьей крупной версией API платформы Windows UI. Целью каждого нового выпуска является попытка упростить функции, представленные в предыдущих обновлениях. Это побуждает разработчиков Universal Windows Platform (UWP) стандартизировать эти функции. Примером этого являются новые скрытые и открытые анимации.

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

Скрытые и открытые анимации для смены страниц

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

 

 

В Creators Update добавлены две новые скрытые техники анимации, которые помогут вам сделать эти переходы более подвижными: 

ElementCompositionPreview.SetImplicitShowAnimation и ElementCompositionPreview.SetImplicitHideAnimation. Всякий раз, когда загружается UIElement или когда для свойства Visibility установлено значение Visible, будет воспроизводиться скрытая анимация, связанная с ним с помощью SetImplicitShowAnimation. Аналогично, всякий раз, когда пользователь переходит от страницы или когда UIElement скрыт, будет вызвана анимация, связанная с использованием метода SetImplicitHideAnimation. Эти две техники облегчают вам возможности добавления движения, как неотъемлемого аспекта всех ваших визуальных элементов, обеспечивая при этом беспрепятственный доступ всем вашим пользователям.

Подключенные анимации

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

 


API-интерфейсы платформы Windows UI предоставляют класс с именем ConnectedAnimationService для координации анимации между исходной страницей и целевой страницей во время навигации. Вы получаете доступ к сервису, вызывая статический метод GetForCurrentView. Затем на исходной странице вы вызываете PrepareToAnimate, передавая уникальный ключ и изображение, которое должно использоваться для анимации перехода.

 

ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("MyUniqueId", image);

 

На целевой странице вы извлекаете изображение из службы ConnectedAnimationService и вызываете TryStart в ConnectedAnimation при передаче в целевом UIElement.

 

var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("MyUniqueId");
if (animation != null)
{
    animation.TryStart(DestinationImage);
};

 

 

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

 

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

 

Скоординированные анимации

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

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

К счастью, координированные анимации также легко реализовать. Метод TryStart класса ConnectedAnimation обеспечивает переопределение, которое позволяет вам вставлять множество визуальных элементов, которые вы хотите оживить скоординированным образом. Предположим, что ваш текст субтитров находится в визуальном элементе, который вы назвали «DescriptionRoot». Вы можете добавить его как скоординированную анимацию, изменив предыдущий код следующим образом:

 

var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("MyUniqueId");
if (animation != null)
{
    animation.TryStart(DestinationImage, new UIElement[] { DescriptionRoot });
};

 

Пользовательские анимации

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

В Creators Update Вы можете использовать пользовательские анимации. Функция пользовательских анимаций позволяет вам модулировать ваши переходы четырьмя способами:

 

  • Crossfade – позволяет вам настроить элементы crossfade, как исходный элемент, которые достигает целевого объекта
  • OffsetX - позволяет настроить X-канал смещения
  • OffsetY - позволяет настроить Y-канал смещения
  • Scale – Позволяет настраивать масштаб элемента при его оживлении

 

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

 

var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("MyUniqueId");
 
var customXAnimation = Window.Compositor.CreateScalarKeyFrameAnimation();
customXAnimation.Duration = ConnectedAnimationService.GetForCurrentView().DefaultDuration;
customXAnimation.InsertExpressionKeyFrame(0.0f, "StartingValue");
customXAnimation.InsertExpressionKeyFrame(0.5f, "FinalValue + 25");
customXAnimation.InsertExpressionKeyFrame(1.0f, "FinalValue");
 
animation.SetAnimationComponent(ConnectedAnimationComponent.OffsetX, customXAnimation);

 

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

Замечательные анимации изображений

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


 

Эта интерполяция происходит автоматически, поэтому вам не о чем беспокоиться.

Поддержка скрытой анимации для наборов свойств и теней

Наконец, возможности анимации были также расширены в Creators Update, позволяя применять скрытые анимации к наборам свойств и теням.

 

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

 

var shadowBlurAnimation = compositor.CreateScalarKeyFrameAnimation();
shadowBlurAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
shadowBlurAnimation.Duration = TimeSpan.FromSeconds(1);
shadowBlurAnimation.Target = "BlurRadius";
 
//Associating animations with triggers 
implicitAnimationShadow["BlurRadius"] = shadowBlurAnimation;
implicitAnimationShadow["Opacity"] = shadowOpacityAnimation;
implicitAnimationShadow["Scale"] = shadowScaleAnimation;
 
implicitAnimationVisual["Translation"] = translationAnimation;
             
 
//Applying Implicit Animations to objects 
content.Properties.ImplicitAnimations = implicitAnimationVisual;
shadow.DropShadow.ImplicitAnimations = implicitAnimationShadow;

 

 

В завершении

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

Чтобы узнать больше о темах, затронутых в этой статье, рекомендуем перейти по ссылкам на следующие статьи и видео: