Страницы

Поиск по вопросам

Показаны сообщения с ярлыком async. Показать все сообщения
Показаны сообщения с ярлыком async. Показать все сообщения

понедельник, 30 марта 2020 г.

Делать ли каскад методов асинхронными, если они могут возвращать просто Task<T>

#c_sharp #net #async_await #async #clr


Терзает вопрос, найти внятный ответ не могу. Гуглится не то.

Вот у меня есть действительно асинхронный метод, в котором несколько await'ов. Этот
метод вызывается на более верхнем уровне:

public async Task UpperLevelMethod()
{
    return await _component.RealyAsyncMethod();
}


Ну и так далее, по всем слоям приложения. 

Но ведь по идее, можно написать так:

public Task UpperLevelMethod()
{
    return _component.RealyAsyncMethod();
}


И вызов await UpperLevelMethod() будет корректно отрабатывать. И, на сколько я понимаю,
этот метод даже лучше, т.к. не будет создаваться дополнительные экземпляры IAsyncStateMachine,
поправьте, если я не прав.

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

Есть ли какие-то подводные камни, у такого вызова? Либо, в случаях, когда не нужно
делать асинхронное продолжение, можно (а возможно даже лучше) делать метод не асинхронным?

P.S. Тот же Скит описывает, что для проверки аргументов лучше делать так:

public Task UpperLevelMethod(string arg)
{
    /*
    Проверка аргумента и выброс исключения в случае ошибки
    */
    return _component.RealyAsyncMethod();
}


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

Update: 
Консольное приложение, собраное в релизе, с таким кодом:

static void Main(string[] args)
{
    Task.Run(Foo5);
}

static async Task Foo1() => await Task.FromResult(5);

static Task Foo2() => Foo1();

static Task Foo3() => Foo2();

static Task Foo4() => Foo3();

static Task Foo5() => Foo4();


весит 8 192 байт, при добавлении await'ов - 10 240 байт.
    


Ответы

Ответ 1



Совершенно верно, лучше не писать лишний раз async если код работает и без него. Нет никаких причин писать лишние слова async и await.

пятница, 13 марта 2020 г.

Несколько параллельных потоков

#c_sharp #wpf #async #async_programming


Есть класс для обработки пула задач. 

public class PoolManager
{
    List>> _listFunc { get; set; }
    List _listName { get; set; }
    public bool Active { get; set; } = false;
    public PoolManager()
    {
        _listFunc = new List>>();
        _listName = new List();
    }

    public async Task StartPool()
    {
        if (!Active)
        {
            Active = true;
            while (Active)
            {
                if (_listFunc.Count != 0)
                {
                    await Task.Run(async () =>
                    {
                        var fun = _listFunc[0];
                        RemoveById(0);
                        await fun();

                    });
                }
            }
        }
       return "";
    }

    public void Add(Func> func,string name)
    {
        _listFunc.Add(new Func> (func));
        _listName.Add(name);
    }

    public void Stop()
    {
        Active = false;
    }
}


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

 if (!VP[0].Active)
 {
      await Task.Run(async () =>
      {
           await VP[0].StartPool();
      });
 }
 else VP[0].Stop();


Но таким образом я могу запустить только 1 такой поток. Как можно реализовать одновременный
запуск нескольких таких потоков с возможностью в последствии обращаться к ним? И соответственно
без блокировки главного потока.
    


Ответы

Ответ 1



Создавать список Task'ов не нужно. Просто надо указать AttachedToParent Task.Factory.StartNew(() => { for(var i=0; i < 10; i++) Task.Factory.StartNew(() => { /*... */}, TaskCreationOptions.AttachedToParent); }).Wait(); Внешний Task дождется завершение всех Task'ов, запущенных в цикле. Если в основном потоке не надо ждать завершения внешнего Task, то Wait не надо указывать. Но при этом этот Task будет ждать завершение работы остальных, запущенных с AttachedToParent. Если требуется передавать данные, то надо использовать классы, например, из System.Collections.Concurrent. Пример - тут.

суббота, 7 марта 2020 г.

Асинхронная загрузка изображений без остановки ui wpf

#c_sharp #wpf #многопоточность #binding #async


Есть стэк, который биндится к списку изображений. 


    
        
            
                
            
        
        
            
                
                    
                
            
        
    



Как мне асинхронно подгружать в него изображения (Большой объем), чтобы ui не останавливался?
Пробовал в коллекцию через асинхронный метод добавлять, но поток все равно останавливается.
Во view добавлял объекту IsAsync, тоже не помогает.

private async void GenerateTop()
{
    Screenshots = new ObservableCollection();
    await Task.Run(() =>
    {
        LastAdded.Add(...)...
    }
}

    


Ответы

Ответ 1



Вы должны разделить модель и представление. Функции загрузки модели должны бежать в неосновном потоке, и там пусть загружают что угодно как угодно медленно. Представление (в случае, если вы используете MVVM, это ваша VM) получает от модели любым способом извещение о том, что данные загрузка произошла и есть новые данные для показа, и (в главном потоке!) обновляет VM-список изображений. Таким образом ваш UI не будет подвисать, ваш код будет будет асинхронным, а волосы — мягкими и шелковистыми. Если подвисает загрузка картинки, попробуйте и правда использовать IsAsync в привязке. Единственная тонкость — у вас сейчас IsAsync грузит асинхронно строку, а конверсия в картинку выполняется в UI-потоке. Попробуйте указать конвертер: class ImageSourceLoadingConverter : IValueConverter { public object Convert(object value, Type targetType, object p, CultureInfo ci) => new BitmapImage(new Uri((string)value)); public object ConvertBack(object value, Type targetType, object p, CultureInfo ci) => throw new NotSupportedException(); } в этом случае, кажется, конверсия будет производиться асинхронно.

Ответ 2



Сделайте обычный метод(не асинхронный). Заполните в нем всю коллекцию и попробуйте так: далее: private void GenerateTop() { Screenshots = new ObservableCollection(); LastAdded.Add(...)... }

пятница, 14 февраля 2020 г.

Task.Run - антипаттерн async/await? C#

#c_sharp #async_await #async #async_programming


Недавно прочитал статью на хабре (upd: из комментов понял, что нужно прицепить цитату,
по которой далее вопрос)


  Как только код доходит до метода Task.Run(), достаётся другой поток из пула потоков
и в нём исполняется код, который мы передали в Task.Run(). Старый поток, как и положено
приличному потоку, возвращается в пул и ждёт, когда его снова позовут делать работу.
Новый поток выполняет переданный код, доходит до синхронной операции, синхронно выполняет
её (ждёт пока операция не будет выполнена) и идёт дальше по коду. Иными словами, операция
так и осталась синхронной: мы, как и раньше, используем поток во время выполнения синхронной
операции. Единственное отличие — мы потратили время на переключение контекста при вызове
Task.Run() и при возврате в ExecuteOperation(). Всё стало немножечко хуже.


Один из вопросов, который там рассматривается: вызов Task.Run - это антипаттерн,
и нужен он только для отзывчивости GUI.

Вопрос именно про Task.Run(() => _anyWork()), где _anyWork() содержит синхронный
код. То, что написано в статье, звучит достаточно логично, если делать так:

await DoWork();
...

Task DoWork() => Task.Run(_work);


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

var task1 = DoWork1();
var task2 = DoWork2();
var task3 = DoWork3();
await Task.WhenAll(task1, task2, task3);


Task.Run сразу превращается в нормальный код, ведь так? 

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

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

Возможно есть какие-то официальные рекомендации MS? На msdn я нашел только сухое
описание работы методов.
    


Ответы

Ответ 1



Один из вопросов, который там рассматривается: вызов Task.Run - это антипаттерн, и нужен он только для отзывчивости GUI. Task.Run Method Ставит в очередь заданную работу для запуска в ThreadPool и возвращает задачу или дескриптор Task для этой работы. То есть Task.Run - это способ выполнить какую-то работу в пуле потоков с возможностью ожидания результата асинхронно. Когда это может понадобится? Возможные примеры использования: 1) Запуск IO/CPU нагрузки пуле потоков, чтобы не грузить основной UI поток (пример) 2) Запуск асинхронного кода синхронно из UI потока. Такое может потребоваться, когда у вас большое приложение и вы постепенно переходите на асинхронные вызовы вместо синхронных, но не везде пока можете вызывать ваш новый API асинхронно, пр этом вам надо избегать дедлоков в UI потоке, например Task.Run(()=>CallSmthgAsync().GetAwaiter().GetResult()).GetAwaiter().GetResult(); - это не оч красивый код и он должен быть исправлен, но не всегода можно внедрить асинхронные вызовы за один заход, потому я такое сам делал и видел иногда. Когда это НЕ надо: когда вас не заботит факт блокировки текущего потока какой то синхронной работой. В прмере в вопросе задача и так уже запущена в пуле потоков, потому переключаться в другой поток и там выполнять что то синхронное смысла не имеет. Но если бы та же работа была запущена из UI потока, то без Task.Run все UI приложение бы встало колом. Что касается библиотечного кода. Стройте ваш API и его реализацию исходя из требований и вариантов использования. Если у вас много I/O операций (работа с файлами, с сетью, с БД), то это по сути самой собой намекает на необходимость асинхронного API. Если вас смущает выбор между синхронным API и асинхронным, то реализуйте оба, пусть клиент выбирает,что ему нужно. Отсюда вывод: самое главное, чтобы не наломать дров, всегда знайте что вы делаете и зачем вы это делаете. Переключайте контекст тогда, когда у вас есть причина это делать. Если каждая строка вашего кода будет обоснована и иметь причину, почему она именно так написана, то проблем с выбором хороший код/ плохой код у вас будет гораздо меньше.

Ответ 2



Код метода *Async выполняется в текущем потоке до первого await. И текущий поток будет захвачен. Например, HttpClient до начала асинхронного запроса синхронно запрашивает dns, что может быть долгой операцией. И вы можете решить, что вас больше беспокоит - ожидание текущего потока или накладные расходы от Task.Run() (которые минимальны по факту, разве что раздувают код)

Ответ 3



использовать Task: если внутри нужно использовать асинхронную операцию: внутри их несколько синхронный код и асинхронная операция вот интересная статья о using(IDisposable) и потенциальном баге если ты делаешь свою собственную асинхронную операцию (например превращаешь событие в Task) - часто используют TaskCompletionSource второе похоже на I/O-bound операцию - не уверен, писать ли отдельным пунктом

воскресенье, 9 февраля 2020 г.

Создать свой Task.Run

#c_sharp #async #lambda #task


Каким образом в c# можно сделать async получив значение во внешний метод из лямбды?

Вот тестовый пример (он нерабочий):

    public async Task Test(MyClass data)
    {
        return Task.Run(() => {

            MyOtherClass result = null;
            bool endFlag = false;

            protocol.Invoke((MyOtherClass response) => // это async
            {
                endFlag = true;
                result = response;
            }, "uri/to/my/rpc", data);

            for (;;) // а это уже await
            {
                Thread.Sleep(200);
                if (endFlag)
                    break;
            }

            return result;

        });
    }


Задача функции Test - вытащить значение response из лямбды, и вернуть его, причём,
сделать это надо асинхронно, к циклу for(;;) хотелось бы перйти только когда потребуется
await. Подскажите, что нужно сделать, и почему этот код студия ругает?


    


Ответы

Ответ 1



Вероятно, вам надо что то вроде public Task Test(MyClass data) { return Task.Run(async () => { MyOtherClass result = null; bool endFlag = false; protocol.Invoke((MyOtherClass response) => // это async { endFlag = true; result = response; }, "uri/to/my/rpc", data); for (; ; ) // а это уже await { await Task.Delay(200); if (endFlag) break; } return result; }); } Но если вызов protocol.Invoke не блокирующий, то внутренний таск нам не нужен, у нас же уже асинхронный код. public async Task Test(MyClass data) { MyOtherClass result = null; bool endFlag = false; protocol.Invoke((MyOtherClass response) => // это async { result = response; endFlag = true; // И лучше бы флагать о конце операции в конце метода, чтобы не возникло проблем с race condition }, "uri/to/my/rpc", data); for (; ; ) // а это уже await { await Task.Delay(200); if (endFlag) break; } return result; } а вот уже совсем короткий вариант, что делает по сути то же самое public Task Test(MyClass data) { var tcs = new TaskCompletionSource(); protocol.Invoke((MyOtherClass response) => // это async { tcs.SetResult(response); }, "uri/to/my/rpc", data); return tcs.Task; }

пятница, 24 января 2020 г.

Map-Reduce на C++ с использованием std::async

#cpp #многопоточность #stl #async #шаблоны_с++


Пытаюсь написать MapReduce.

#include 
#include 
#include 
#include 
#include 

template 
void map(It first, It last, Func f)
{
    while (first != last)
    {
        f(*first++);
    }
}

template 
auto map_reduce(It first, It last, MapFunc f1, FoldFunc f2, size_t num_threads)
    -> decltype(f2(f1(*first), f1(*first)))
{
    size_t block_size = std::distance(first, last) / num_threads;

    std::vector> results(num_threads);
    for (size_t i = 0; i < num_threads; ++i)
    {
        results[i] = std::async(map, first, last, f1);
    }

    return 0;
}


int main()
{
    std::list ls = { 1, 2, 3 };
    auto sum = map_reduce(ls.begin(), ls.end(),
        [](int i) {return i; },
        std::plus(),
        3);

    return 0;
}


Почему компилятор ругается на вызов std::async?

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


Ответы

Ответ 1



Вы передаете шаблонную функцию map в шаблон же, и компилятор не понимает, что туда надо подсунуть. Можно попробовать так: results[i] = std::async(map, first, last, f1); только вот у вас results - это фьючерсы future, а ваша map возвращает void... Если исправить и это, то скомпилироваться она скомпилируется, ну, а что она делать будет - это я не смотрел.

четверг, 23 января 2020 г.

Javascript: коллбэки, асинхронность и разделение кода

#javascript #callback #cordova #async


Здравствуйте.

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

CordovaWrapper.getRawData(successCallback, errorCallback);


Данные, которые я получу из cordova (они придут как аргументы successCallback), я
бы хотел использовать в HTML приложении. 

Конечно, можно во всех местах, где нужны данные из successCallback, использовать
этот CordovaWrapper, чтобы получить данные и сразу же использовать:

CordovaWrapper.getRawData(function(JSONString) {
    var data = JSON.parse(JSONString)
    for (var i in data) {
        data[i].formattedValue = ''+data[i].value+'';
    }

    // здесь мы используем эти полученные и отформатированные данные

}, function(e) {
    console.log(e)
});


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

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

    if (standaloneMode) {
        // какие-то тестовые данные
        return [{
            value: 'test1',
            options: [...],
        }, {
            value: 'test2',
            options: [...],
        }];
    } else {
        return dataFromCordova;
    }


Проблема в том, что получение данных - асинхронное.

Если делать асинхронно, получается коллбэк на коллбэке:

Объявление:

DataProvider.getData = function(callback) {
    var format = function (arr) {
        for (var i in arr) {
            arr[i].formattedValue = ''+arr[i].value+'';
        }
        return arr;
    };

    if (standaloneMode) {
        var testData = [{
            value: 'test1',
            options: [...],
        }, {
            value: 'test2',
            options: [...],
        }];

        callback(format(testData));
        return;
    }

    if (this.cache) {
        callback(this.cache);
    } else {
        var successCallback = function(JSONString) {
            data = JSON.parse(JSONString);
            callback(format(data));
        };
        CordovaWrapper.getRawData(successCallback, errorCallback);
    }
}


И использование:

DataProvider.getData(function (data) {
    //  здесь мы используем эти полученные и отформатированные данные
});


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

Как правильно сделать? Нормально ли это, хотеть отдельный класс/объект, который будет
предоставлять данные? Нормально ли иметь столько коллбэков из-за асинхронности? Как
делать такого рода кеширование? Как не дублировать строчки вида

callback(format(data));


?
    


Ответы

Ответ 1



JavaScript это ивентво ориентированный язык, то есть лапша из колбеков это обычное дело. К сожелению текущая версия JavaScript не позволяет упростить работу с колбеками, и для этого вам необходимо подобрать библиотеку для упрощения логики работы с событиями. Замечу что если у вас в приложении действительно много колбеков то возможно стоит ознакомиться с Функциональным реактивным программированием. Вот очень мотивирующи доклад к изучению FRP. Но в большинстве случаев FRP будет излишним, и можно воспользоваться Promise (на самом деле это частный случай FRP). Промисы будут достепны в ES6 а пока вы можете воспользоваться реализацией промисов из какой-нибудь библиотеки например jquery Promise - это специальный объект который выпонит успешный callback или callback с ошибкой и гарантирует что один из этих колбеков будет выполнен и выполнен только один раз. На основании этих условий есть разные средства для комбинирования промисов, например метод when из jquery. Если вам нужно больше вариантов для комбинирования промисов, посмотрите библиотеку Q. По поводу вашего вопроса с кешированием ответа я приведу пример с использованием jquery, в других библотеках решение не должно сильно отличаться. В jquery есть класс Deferred который помогает создавать промисы. Вам нужно создать объект Deferred var deferred = new $.Deferred() например в вашем врапере и вернуть промис deferred.promise() В вашем врапере в момент получения данных необходимо вызвать метод resolve если получение данных было успешным или reject в противном случае. У обекта который вы получите в результате метода deferred.promise() будут методы done и fail которые принимают callback. Обратите внимание что колбек вызовится в любом случае, даже если получение данных произошло до добавления колбеков в методы done и fail. Таким образом вы можете создать 1 объект Deferred для каждого запроса в функции получения данных и возвращать промис каждый раз при обрашении к функции. В итоге вы получите кеширование данных и избавитесь от лапшы колбеков. Примеры Deferred Статья про промисы

суббота, 11 января 2020 г.

c# множество асинхронных HttpWebRequest [дубликат]

#c_sharp #async #httpwebrequest


        
             
                
                    
                        
                            На этот вопрос уже дан ответ здесь:
                            
                        
                    
                
                        
                            Увеличить скорость парсера
                                
                                    (1 ответ)
                                
                        
                                Закрыт 2 года назад.
            
                    
Пытаюсь сделать множество асинхронных запросов HttpWebRequest. Подготовил мини тест:

class Program
{
    static void Main(string[] args)
    {
        Test();
        Console.ReadLine();
    }

    public static async void Test()
    {
        for (int i = 0; i < 10; i++)
        {
            int val = i;
            await Task.Run(() => WR(val));
        }
    }

    static async void WR(int msg)
    {
        Console.WriteLine(msg + " begin");

        string url = "https://stackoverflow.com";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "GET";
        var response = (HttpWebResponse)await Task.Factory.FromAsync
                (request.BeginGetResponse, request.EndGetResponse, null);

        Console.WriteLine(msg + " status code: " + response.StatusCode);
        Console.WriteLine(msg + " end");
    }
}


Но вот что получилось:

0 begin
1 begin
2 begin
3 begin
4 begin
5 begin
6 begin
7 begin
8 begin
9 begin
0 status code: OK
0 end
1 status code: OK
1 end


А после 1 end вообще ничего не происходит. Где-то через 30 секунд в output вылазит:

The thread 0x6634 has exited with code 0 (0x0).
The thread 0x5620 has exited with code 0 (0x0).
The thread 0x4d08 has exited with code 0 (0x0).
The thread 0x39b8 has exited with code 0 (0x0).
The thread 0x3454 has exited with code 0 (0x0).
The thread 0x99c has exited with code 0 (0x0).
The thread 0x6be0 has exited with code 0 (0x0).


Но никаких ошибок в дебаге не вываливается. Подскажите где я ошибся и как исправить?

UPDATE:
Затык происходит при запуске в Visual Studio. Интересно что с включенным Fiddler
все работает нормально. 
    


Ответы

Ответ 1



Переписал немного ваш код. Все прекрасно работает void Main() { Test(); Console.ReadLine(); } public static async Task Test() { var tasks = new List(); for (int i = 0; i < 10; i++) { int val = i; var task = WR(val); tasks.Add(task); } await Task.WhenAll(tasks); } static async Task WR(int msg) { Console.WriteLine(msg + " begin"); string url = "https://stackoverflow.com"; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = "GET"; var response = (HttpWebResponse)await Task.Factory.FromAsync (request.BeginGetResponse, request.EndGetResponse, null); Console.WriteLine(msg + " status code: " + response.StatusCode); Console.WriteLine(msg + " end"); response.Dispose(); } Вывод 0 begin 1 begin 2 begin 3 begin 4 begin 5 begin 6 begin 7 begin 8 begin 9 begin 3 status code: OK 3 end 5 status code: OK 5 end 0 status code: OK 0 end 6 status code: OK 6 end 2 status code: OK 2 end 7 status code: OK 7 end 1 status code: OK 1 end 8 status code: OK 8 end 9 status code: OK 9 end 4 status code: OK 4 end UPD Не забываем диспозить респонс static async Task WR(int msg) { Console.WriteLine(msg + " begin"); string url = "https://stackoverflow.com"; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = "GET"; using (var response = (HttpWebResponse) await Task.Factory .FromAsync(request.BeginGetResponse, request.EndGetResponse, null)) //// <<<<< { Console.WriteLine(msg + " status code: " + response.StatusCode); Console.WriteLine(msg + " end"); } }

четверг, 9 января 2020 г.

Стоит ли заменить подход std:thread на std::async?

#cpp #многопоточность #async #многоагентная_система


В проекте (многоагентная система для работы с потоком разнотиповых данных в реальном
времени) используется многопоточность из стандартной библиотеки C++, а именно подход
на основе потоков std::thread.

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

Прочитал недавно про подход на основе задач std::async.

Вопросы:


Может ли мне дать какое-то преимущество этот подход?
И какие сложности могут возникнуть в рамках описанной системы?
Где прочитать про эту технологию? Я смотрел только пару обзорных статей.

    


Ответы

Ответ 1



Эффективность std::async сильно зависит от реализации. В старых версиях gcc, например, std::async вообще всегда работает в том же потоке, где вызывается future.get(), несмотря на флаг launch_async. Но даже если реализация хорошая, в лучшем случае std::async будет отправлять задачу в обычный пул потоков. В этом случае проще взять сразу готовый пул потоков и работать с ним, тем более есть пулы потоков с поддержкой std::future, что делает их интерфейс похожим на std::async. Одна из реализаций есть тут. Возможно, для вашей задачи будет удобно использовать в качестве пула потоков boost::asio, поскольку помимо обычных возможностей пула потоков там есть таймеры и асинхронный ввод-вывод (ради чего библиотека и создавалась).

пятница, 3 января 2020 г.

Почему PauseToken прерывает Task?

#c_sharp #wpf #async


Пытаюсь разобраться с PauseToken (Namespace: Microsoft.VisualStudio.Workspace).

Не могу понять когда вызываю PauseTokenSource.Pause(); PauseToken уходит в паузу
и одновременно завершает task и я получаю сообщение "Completed".

Но при этом если после PauseTokenSource.Pause(); вызвать PauseTokenSource.Resume();,
то task продолжит работать, но по окончании никаких сообщений не будет.

Два вопроса почему так происходит, и как это можно поправить?

public PauseToken PauseToken;

private async Task DoDoneAsync()
{
    FlagCommandDone = true;

    try
    {
        task = Task.Factory.StartNew(async () =>
            {
                double i = 0;
                while (i < 3)
                {
                    i++;
                    await PauseToken.WaitWhilePausedAsync();
                    Thread.Sleep(5000);
                }
            }
        );
        await task;
    }
    catch (Exception e)
    {
        isError = true;
        message = e.Message;
    }

    if (!isError) message = task.IsCompleted ? "Completed" : "Canceled";

    MessageBox.Show(message);

    FlagCommandDone = false;
}

    


Ответы

Ответ 1



Проблема в Task.Factory.StartNew. Этот метод запускает обыкновенные функции как Task, но для асинхронных он делает не то, что вы ожидаете. Используйте Task.Run вместо него. Давайте вспомним, как выполняются асинхронные функции. Ваш async-делегат с точки зрения внешнего кода не выполняется до конца, а завершается с первым внутренним await'ом, возвращая Task (который, в свою очередь, завершится с окончанием выполнения всей асинхронной функции). Следовательно, ваш task является не заданием выполнения всей асинхронной функции, а лишь заданием получения на неё Task'а! Действительно, сигнатура вашего async-делегата есть Task f(), а сигнатура функции, возвращённой из Task.Factory.StartNew — Task f(). Task.Run обрабатывает эту ситуацию более ожидаемым образом: он получает Task, и делает внутри await, таким образом, вы дожидаетесь «внутреннего» таска, представляющего собой ваш асинхронный делегат. Литература: PFX team, Task.Run vs Task.Factory.StartNew (кажется, русский перевод тут).

пятница, 20 декабря 2019 г.

Непонятности с асинхронным кодом

#c_sharp #асинхронность #async #async_programming #async_await


Читаю вот эту статью про использование Task и async-await в C# 
В ней приведён такой код

var client = new WebClient();
var task = client.DownloadStringTaskAsync("/api/blabla");
Console.WriteLine("Hello world!");
var result = await task;
Console.WriteLine("Got some data");


и далее про него говорится вот что


  Почему этот код хороший и правильный? Потому что DownloadStringTaskAsync возвращает
Task, который инкапсулирует операцию ввода-вывода — то есть I/O bound операцию. И практически
весь ввод-вывод является асинхронным — то есть, для его осуществления, нигде, начиная
с самого верхнего уровня вызова метода DownloadStringTaskAsync и заканчивая драйвером
сетевой карты, абсолютно нигде не нужен дополнительный поток, который будет «ждать»
или «обрабатывать» эту операцию.


Точно так же приводится пример из JS/jQuery

$.get('/api/blabla', function(data) {
  console.log("Got some data.");
});
console.log("Hello world!")


и комментарий к нему 


  Это — очень яркий пример асинхронного программирования. Здесь нет никакой многопоточности
— Javascript строго однопоточен


В обоих случаях особо отмечается что многопоточности тут нет. И подобное я читал
также и в других источниках что при использовании async-await (и асинхронности вообще)
далеко не всегда подразумевает использование дополнительного потока. И это мне совершенно
непонятно.  Возьмем тот же пример на jQuery. Сначала выполняется запрос, у которого
есть коллбэк а потом выводится сообщение "Hello world!". Но так как запрос выполняется
асинхронно то "Hello world!" выводится раньше. И мне непонятно как тут обойтись без
нескольких потоков? Ведь если асинхронный запрос выполняется в одном потоке с основным,
то по идее и асинхронности быть не должно потому что поток сначала должен дождаться
выполнения запроса а только потом вывести 
"Hello world!". То же самое и по C# коду. Объясните пожалуйста почему автор так упорно
говорит что многопоточности тут нет? Заранее спасибо!
    


Ответы

Ответ 1



Дело в том, что сетевые и дисковые вызовы в Windows поддерживают так называемый Overlapped I/O, который позволяет получить уведомление по завершению операции. Т.е. во время выполнения операции с диском или сетью пользовательский поток может делать все что угодно, а не обязательно спать и ждать результатов. Как работает обычное, неасинхронное чтение: Ваш код делает синхронный вызов Поток, сделавший вызов, засыпает Данные читаются из диска или из сети Поток просыпается Вызов возвращает результат Как работает асинхронное чтение Ваш код делает асинхронный вызов Поток, сделавший вызов, освобождается - возвращается в пул Данные читаются из диска или из сети Приложение получает уведомление от системы о получении данных (через I/O Completion Port) Рантайм находит код, который должен обработать результат - продолжение вашего async-метода и ставит его на выполнение. Ваш код возвращает результат Разница в том, что между 2 и 6 в асинхронном коде никакой поток не спит в ожидании результата. А потоки в Win - это достаточно ценный ресурс. Их тяжело (относительно тяжело) запустить. Они требуют памяти (около 1Mb на поток). Их надо синхронизировать - чем больше потоков, тем больше накладных расходов. Кроме того, если между 2 и 6 в вашем коде есть что-то, что можно выполнить (например, вы не сразу потребовали результат чтения с диска или из сети) - то это будет выполнено потоком, вместо пустого ожидания. При этом будет одновременность выполнения вашего кода, и физического чтения данных из сети. Но многопоточности все еще не будет - поток то у вас останется ровно один. То, что при этом драйвер сетевой карты читает данные - не добавляет еще один поток в ваше приложение. И вы можете спокойно обойтись без синхронизации, и прочих неприятностей, связанных с многопоточностью. Абстракция, как же без нее: многопоточное изготовление пиццы - склонировать себя и заставить клона готовить пиццу. по завершению - слиться в одного человека. асинхронное изготовление пиццы - позвонить в доставку, спокойно делать свои дела, по звонку в дверь открыть и забрать готовую пиццу.

Ответ 2



Многопоточности нет потому, что ожидание загрузки в DownloadStringTaskAsync не занимает отдельного потока! Происходит следующее: Запускается DownloadStringTaskAsync, отправляется GET-запрос. Из DownloadStringTaskAsync возвращается неоконченный Task. Когда-нибудь приходит ответ на GET-запрос, в этот момент Task переходит в состояние «окончен». await получает управление, присваивает результат. Между пунктами 2 и 3 код бежит в никаком потоке: он представлен лишь структурами данных в памяти, но не занимает никакой поток.

четверг, 19 декабря 2019 г.

Многопоточность и ассинхронность

#java #многопоточность #async


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

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

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

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

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

В общем, объясните пожалуйста
    


Ответы

Ответ 1



Прежде всего надо разобраться с терминологией. В самом простом виде программа выполняет последовательно или синхронно, инструкция за инструкцией, в том порядке, в котором они идут в коде. System.out.println("A"); System.out.println("B"); System.out.println("C"); Каждый вызов println блокирующийся. Это значит, что пока он не закончится, программа не будет выполняться дальше. В реальном мире от программы требуется реагировать на множество событий, происходящих в произвольные моменты времени, или одновременно выполнить задачи существенно более долгие, чем вывод на экран. Такое выполнение называется многозадачным. Но в этом месте часто возникает путаница, так как вместо этого часто, особенно в мире Java, используют термин "асинхронное выполнение", а у этого термина есть своё значение, более конкретное. Чтобы лучше разобраться в истинном значении терминов, приведу бытовую аналогию: Один человек ставит кастрюлю на плиту, ждёт пока она закипит, закидывает пельмени, ждёт пока сварятся, снимает, ставит вторую, ждёт пока закипит... - последовательное однопоточное выполнение. Один человек ставит две кастрюли на плиту, как только одна из них закипела, забрасывает пельмени и т.д. - асинхронное выполнение. Два человека ставят две кастрюли... - многопоточное выполнение. В данной аналогии плита - это аппаратный ресурс (процессор), пельмени - программный ресурс (сокеты), человек - это поток, а кипение - блокирующийся вызов. Примечание: К аналогии можно добавить ещё вариант "Два человека ставят две кастрюли на две плиты", описывающий действительную параллельность в многопроцессорной системе. Но с описанием видов многозадачности можно легко выйти за ограничение по количеству символов на ответ. Очевидно, что при асинхронном выполнении ресурсов требуется меньше, а скорость та же, что и при многопоточном. Именно поэтому проблему c10k могут решать только асинхронные сервера. Минусы в том, что асинхронный код зачастую сложнее и не всё можно выполнять асинхронно. В вашем прошлом вопросе я уже показывал примеры многопоточного и асинхронного кода: Многопоточный код ExecutorService executor = Executors.newFixedThreadPool(10); while (true) { // Вызов accept() блокирующийся и остановит цикл до тех пор // пока не поступит новое соединение SocketChannel socketChannel = serverSocketChannel.accept(); // При этом будет запущен отдельный поток, который будет // обслуживать только это соединение executor.execute(() -> { // Отправка файл, которая займёт 5 секунд например }); // Выполнение продолжится сразу после вызова execute, // меньше чем через миллисекунду. Если подключится ещё один // клиент, для него будет запущен ещё один поток, и будут // передаваться два файла одновременно двум разным клиентам. } Асинхронный код serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // Цикл событий selector.select(); // Мультиплексирование Set keys = selector.selectedKeys(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if (key.isAcceptable()) { // Поступило новое соединение SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); // Регистрируем его для обработки в этом же потоке // на следующих итерациях цикла socketChannel.register(selector, SelectionKey.OP_READ, new Storage()); } if (key.isReadable()) { // В одном из соединений появились данные SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(256); // Читаем столько данных, сколько сейчас есть int read = socketChannel.read(buffer); if (read != -1) { ... } else { // Ничего не прочиталось, значит пора закрывать соединение socketChannel.close(); } } iterator.remove(); } } В примере асинхронного кода нет ни одного блокирующегося вызова, цикл никогда не останавливается. Какой вариант многозадачности выбрать - это вопрос имеющий смысл только в контексте конкретной задачи. У каждого варианта свои плюсы и минусы. Из примеров видно, что асинхронного кода требуется больше, но при асинхронном выполнении значительно реже возникает ситуация, когда два человека дерутся за одну пустую кастрюлю, пока в другой догорают пельмени.

воскресенье, 24 ноября 2019 г.

Многопоточное vs асинхронное программирование


Хотелось бы узнать разницу между этими подходами.
Разве асинхронное программирование не подразумевает из себя уже многопоточность
ведь Task где-то там по любому выполняется в отдельном потоке ?

В каких случаях нужно прибегать к многопоточному, а в каких к асинхронному программированию ?

И еще ко всему этому есть параллельное программирование, которая тоже вносит путаниц
для меня. В чем её отличие ?
    


Ответы

Ответ 1



Попробую собрать воедино все, что дали уже в комментариях. Есть несколько разных понятий, связанных с областью параллельных вычислений. Конкурентное исполнение (concurrency) Параллельное исполнение (parallel execution) Многопоточное исполнение (multithreading) Асинхронное исполнение (asynchrony) Каждый из этих терминов строго определен и имеет четкое значение. Конкурентность (concurrency) Конкурентность (*) (concurrency) - это наиболее общий термин, который говорит, чт одновременно выполняется более одной задачи. Например, вы можете одновременно смотрет телевизор и комментить фоточки в фейсбуке. Винда, даже 95-я могла (**) одновременн играть музыку и показывать фотки. (*) К сожалению, вменяемого русскоязычного термина я не знаю. Википедия говорит что concurrent computing - это параллельные вычисления, но как тогда будет paralle computing по русски? (**) Да, вспоминается анекдот про Билла Гейтса и многозадачность винды, но, теоретическ винда могла делать несколько дел одновременно. Хотя и не любых. Конкурентное исполнение - это самый общий термин, который не говорит о том, каки образом эта конкурентность будет получена: путем приостановки некоторых вычислительны элементов и их переключение на другую задачу, путем действительно одновременного исполнения путем делегации работы другим устройствам или еще как-то. Это не важно. Конкурентное исполнение говорит о том, что за определенный промежуток времени буде решена более, чем одна задача. Точка. Параллельное исполнение Параллельное исполнение (parallel computing) подразумевает наличие более одного вычислительног устройства (например, процессора), которые будут одновременно выполнять несколько задач. Параллельное исполнение - это строгое подмножество конкурентного исполнения. Эт значит, что на компьютере с одним процессором параллельное программирование - невозможно;) Многопоточность Многопоточность - это один из способов реализации конкурентного исполнения путе выделения абстракции "рабочего потока" (worker thread). Потоки "абстрагируют" от пользователя низкоуровневые детали и позволяют выполнят более чем одну работу "параллельно". Операционная система, среда исполнения или библиотек прячет подробности того, будет многопоточное исполнение конкурентным (когда потоко больше чем физических процессоров), или параллельным (когда число потоков меньше ил равно числу процессоров и несколько задач физически выполняются одновременно). Асинхронное исполнение Асинхронность (asynchrony) подразумевает, что операция может быть выполнена кем-т на стороне: удаленным веб-узлом, сервером или другим устройством за пределами текущег вычислительного устройства. Основное свойство таких операций в том, что начало такой операции требует значительн меньшего времени, чем основная работа. Что позволяет выполнять множество асинхронны операций одновременно даже на устройстве с небольшим числом вычислительных устройств. CPU-bound и IO-Bound операции Еще один важный момент, с точки зрения разработчика - разница между CPU-bound и IO-boun операциями. CPU-Bound операции нагружают вычислительные мощности текущего устройства а IO-Bound позволяют выполнить задачу вне текущей железки. Разница важна тем, что число одновременных операций зависит от того, к какой категори они относятся. Вполне нормально запустить параллельно сотни IO-Bound операций, и надеяться что хватит ресурсов обработать все результаты. Запускать же параллельно слишком большо число CPU-bound операций (больше, чем число вычислительных устройств) бессмысленно. Возвращаясь к исходному вопросу: нет смысла выполнять в 1000 потоков метод Calc если он является CPU-Intensive (нагружает центральный процессор), поскольку это приведе к падению общей эффективности вычислений. ОС-ке придется переключать несколько доступны ядер для обслуживания сотен потоков. А этот процесс не является дешевым. Самым простым и эффективным способом решения CPU-Intensive задачи, заключается использовании идиомы Fork-Join: задачу (например, входные данные) нужно разбить на определенно число подзадач, которые можно выполнить параллельно. Каждая подзадача должна быть независимо и не обращаться к разделяемым переменным/памяти. Затем, нужно собрать промежуточны результаты и объединить их. Именно на этом принципе основан PLINQ. О чем можно почитать тут: Джозеф Албахари Параллельное программирование. Выглядит это очень интересно: IEnumerable yourData = GetYourData(); var result = yourData.AsParallel() // начинаем обрабатывать параллельно .Select(d => ComputeMD5(d)) // Вычисляем параллельно .Where(md5 => IsValid(md5)) .ToArray(); // Возврвщаемся к синхронной модели В этом случае, число потоков будет контролироваться библиотечным кодом в недрах CLR/TP и метод ComputeMD5 будет вызван параллельно N-раз на компьютере с N-процессорами (ядрами).

Ответ 2



Многопоточное программирование подразумевает, что код приложения выполняется в разны потоках. Например, есть главный поток UI, и несколько рабочих потоков, которые выполняю тяжелые вычисления, результаты которых затем выводятся на UI. Асинхронное программирование подразумевает инициацию некоторой операцию, об окончани которой главный поток узнает спустя некоторое время. Обычно это применяется для работ с системой ввода-вывода: диски, сеть и т.д. При этом, если это все сделано правильно никакого потока нет. Также часто под выражением "выполнить асинхронно" подразумевают что выполнение некоторого кода будет произведено не в текущем потоке, а в соседнем при этом текущий поток не будет заблокирован. Но мой взгляд, это не совсем корректно. Параллельное программирование подразумевает разбиение одной задачи на независимы подзадачи, которые можно рассчитать параллельно, а затем объединить результаты. Оди из примеров -- это map-reduce. Это частный случай многопоточного программирования.

Ответ 3



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

Ответ 4



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

вторник, 16 июля 2019 г.

Как сделать это асинхронным?

Приложение NodeJS. Есть вот такой кусочек кода:
var isAccepted; offers.getOffer({tradeOfferId: offer.tradeofferid}, function (err, o) { if (err || (o == null) || (typeof o.response.offer.trade_offer_state == "undefined")) { helper.msg('Cant load offer state. It can be accepted, but we cant check it. Returning'); isAccepted = false; return; } if (o.response.offer.trade_offer_state == "3") { isAccepted = true; } else { isAccepted = false; } return; });
if (!isAccepted) { io.emit('tradeoffer-msg', { steamid: offer.steamid_other, type: 6 }); return; }
Проблема в том, что if (!isAccepted) выполняется раньше, чем мы получаем результат от сервера в offers.getOffer. Я, конечно, мог бы поместить
if (!isAccepted) { io.emit('tradeoffer-msg', { steamid: offer.steamid_other, type: 6 }); return; }
в offers.getOffer, но тогда return будет работать на безымянную функцию в offers.getOffer, а не "снаружи" как мне и нужно. Вопрос: как заставить приложение ждать ответа в данном случае?


Ответ

Асихронный return вы не сделаете, так не бывает. Приведите больше кода, т.к. проблема не в этом кусочке. Вам нужно вместо return использовать callback функцию в каком-либо варианте
var isAccepted; offers.getOffer({tradeOfferId: offer.tradeofferid}, function (err, o) { if (err || (o == null) || (typeof o.response.offer.trade_offer_state == "undefined")) { helper.msg('Cant load offer state. It can be accepted, but we cant check it. Returning'); isAccepted = false; return; } if (o.response.offer.trade_offer_state == "3") { isAccepted = true; } else { isAccepted = false; } if (!isAccepted) { io.emit('tradeoffer-msg', { steamid: offer.steamid_other, type: 6 }); callback() } });
P.S. Можно конечно сделать так, чтоб этот код выполнялся как будто синхронно, но такие штуки пишут раки для крабов.

суббота, 13 июля 2019 г.

C++ асинхронный бесконечный цикл

Здравствуйте. У меня есть бесконечный цикл. При запуске, как и ожидалось, программка виснет, ничего не нажимается. Как можно запустить этот цикл асинхронно? В C++ не очень силен, мне нужен простенький пример. Спасибо)
for (size_t i = 0; i < INFINITY; i++) { somefunction(); }


Ответ

Используя std::thread, сделал бесконечный цикл внутри отдельного потока, которому можно передать в указатель значение false и тогда цикл в потоку остановиться.
bool *ptrStatus = &status; *ptrStatus = false;
Переменные status и interval имеют глобальную зону видимости.
Код:
bool status = true; int interval = 1;
void startNewThread(int interval) { thread newThread(setWallpaper, &status, interval); newThread.detach(); };
int setWallpaper(bool *status, int interval) { while (true) { if (*status == false) return 0; wstring path = getPath(); SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (PVOID)path.c_str(), SPIF_UPDATEINIFILE); this_thread::sleep_for(chrono::seconds(interval)); } }

среда, 10 июля 2019 г.

Как передать коллекцию прямоугольников в ItemsControl с Canvas асинхронно?

При решении вопроса, возник новый.
Что делаю: из ViewModel передаю коллекцию прямоугольников, вот так:
public async void Start() { RectItems.Clear();
CrossStitch cs = new CrossStitch() { BlockSize = _blockSize, Source = _sourceImage };
var data = await cs.Create();
foreach (var r in data) RectItems.Add(r);
}
Получаю во View вот так:

Все замечательно работало, до того как метод Start() стал async. Теперь я получаю вместо результата, это:
Необходимо создать DependencySource в том же потоке, в котором создан DependencyObject.
Нашел только одну похожую проблему, но в ней передавалось изображение в Canvas, и проблема решалась вызовом Freeze у изображения. А как быть в моем случае?
Метод Create и прилежащие:
public Task> Create() { return PixelateAsync(_source); }
private Task> PixelateTask(Bitmap source) { return Task.Factory.StartNew(() => Pixelate(source)); }
private Task> PixelateAsync(Bitmap source) { return PixelateTask(source); }
Pixelate(source) синхронный.
Класс RectItem
public class RectItem { public double X { get; set; } public double Y { get; set; } public double Width { get; set; } public double Height { get; set; } public System.Windows.Media.Brush C { get; set; } }
Метод Pixelate весь:
private List Pixelate(Bitmap source) { var result = new Bitmap(source);
List rectangs = new List();
using (var graphics = Graphics.FromImage(result)) { graphics.PageUnit = GraphicsUnit.Pixel;
for (int x = 0; x < source.Width; x += _blockSize) { for (int y = 0; y < source.Height; y += _blockSize) { var sums = new Sums();
for (int xx = 0; xx < _blockSize; ++xx) { for (int yy = 0; yy < _blockSize; ++yy) { if (x + xx >= source.Width || y + yy >= source.Height) { continue; }
var color = source.GetPixel(x + xx, y + yy); sums.A += color.A; sums.R += color.R; sums.G += color.G; sums.B += color.B; sums.T++; } }
var average = Color.FromArgb( sums.A / sums.T, sums.R / sums.T, sums.G / sums.T, sums.B / sums.T);
average = GetNearestColor(average); System.Windows.Media.Color mcolor = System.Windows.Media.Color.FromArgb(average.A, average.R, average.G, average.B); System.Windows.Media.Brush brush = new System.Windows.Media.SolidColorBrush(mcolor); rectangs.Add(new RectItem() { X = x + BlockSize, Y = y + BlockSize, Height = BlockSize, Width = BlockSize, C = brush }); } } }
return rectangs; }


Ответ

Смотрите. Проблема в том, что VM-классы создаются в фоновом потоке, это в обычной ситуации неправильно.
Но в вашем случае RectItem — не DependencyObject, а значит, он не привязан к определённому потоку. Поэтому можно пойти более простым путём: создавать этот объект где угодно. Единственная проблема, которую нужно вынести в UI — создание Brush. Но Brush является Freezable- значит, его можно также создавать где угодно, просто нужно после создания вызвать brush.Freeze();
Ещё один framework-класс — Color — тоже не является проблемой, т. к. он не является ни DependencyObject'ом, ни Freezable
Итого: просто добавьте после
System.Windows.Media.Brush brush = new System.Windows.Media.SolidColorBrush(mcolor);
строку
brush.Freeze();

понедельник, 3 июня 2019 г.

Несколько параллельных потоков

Есть класс для обработки пула задач.
public class PoolManager { List>> _listFunc { get; set; } List _listName { get; set; } public bool Active { get; set; } = false; public PoolManager() { _listFunc = new List>>(); _listName = new List(); }
public async Task StartPool() { if (!Active) { Active = true; while (Active) { if (_listFunc.Count != 0) { await Task.Run(async () => { var fun = _listFunc[0]; RemoveById(0); await fun();
}); } } } return ""; }
public void Add(Func> func,string name) { _listFunc.Add(new Func> (func)); _listName.Add(name); }
public void Stop() { Active = false; } }
Всё работает, но есть необходимость запускать несколько паралельных таких пулов. запускаю я его так:
if (!VP[0].Active) { await Task.Run(async () => { await VP[0].StartPool(); }); } else VP[0].Stop();
Но таким образом я могу запустить только 1 такой поток. Как можно реализовать одновременный запуск нескольких таких потоков с возможностью в последствии обращаться к ним? И соответственно без блокировки главного потока.


Ответ

Создавать список Task'ов не нужно. Просто надо указать AttachedToParent
Task.Factory.StartNew(() => { for(var i=0; i < 10; i++) Task.Factory.StartNew(() => { /*... */}, TaskCreationOptions.AttachedToParent); }).Wait();
Внешний Task дождется завершение всех Task'ов, запущенных в цикле. Если в основном потоке не надо ждать завершения внешнего Task, то Wait не надо указывать. Но при этом этот Task будет ждать завершение работы остальных, запущенных с AttachedToParent.
Если требуется передавать данные, то надо использовать классы, например, из System.Collections.Concurrent. Пример - тут

среда, 22 мая 2019 г.

Асинхронная загрузка изображений без остановки ui wpf

Есть стэк, который биндится к списку изображений.

Как мне асинхронно подгружать в него изображения (Большой объем), чтобы ui не останавливался? Пробовал в коллекцию через асинхронный метод добавлять, но поток все равно останавливается. Во view добавлял объекту IsAsync, тоже не помогает.
private async void GenerateTop() { Screenshots = new ObservableCollection(); await Task.Run(() => { LastAdded.Add(...)... } }


Ответ

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

Если подвисает загрузка картинки, попробуйте и правда использовать IsAsync в привязке. Единственная тонкость — у вас сейчас IsAsync грузит асинхронно строку, а конверсия в картинку выполняется в UI-потоке. Попробуйте указать конвертер:
class ImageSourceLoadingConverter : IValueConverter { public object Convert(object value, Type targetType, object p, CultureInfo ci) => new BitmapImage(new Uri((string)value));
public object ConvertBack(object value, Type targetType, object p, CultureInfo ci) => throw new NotSupportedException(); }
в этом случае, кажется, конверсия будет производиться асинхронно.

воскресенье, 14 апреля 2019 г.

Создать свой Task.Run

Каким образом в c# можно сделать async получив значение во внешний метод из лямбды?
Вот тестовый пример (он нерабочий):
public async Task Test(MyClass data) { return Task.Run(() => {
MyOtherClass result = null; bool endFlag = false;
protocol.Invoke((MyOtherClass response) => // это async { endFlag = true; result = response; }, "uri/to/my/rpc", data);
for (;;) // а это уже await { Thread.Sleep(200); if (endFlag) break; }
return result;
}); }
Задача функции Test - вытащить значение response из лямбды, и вернуть его, причём, сделать это надо асинхронно, к циклу for(;;) хотелось бы перйти только когда потребуется await. Подскажите, что нужно сделать, и почему этот код студия ругает?


Ответ

Вероятно, вам надо что то вроде
public Task Test(MyClass data) { return Task.Run(async () => {
MyOtherClass result = null; bool endFlag = false;
protocol.Invoke((MyOtherClass response) => // это async { endFlag = true; result = response; }, "uri/to/my/rpc", data);
for (; ; ) // а это уже await { await Task.Delay(200); if (endFlag) break; }
return result;
}); }
Но если вызов protocol.Invoke не блокирующий, то внутренний таск нам не нужен, у нас же уже асинхронный код.
public async Task Test(MyClass data) { MyOtherClass result = null; bool endFlag = false;
protocol.Invoke((MyOtherClass response) => // это async { result = response; endFlag = true; // И лучше бы флагать о конце операции в конце метода, чтобы не возникло проблем с race condition }, "uri/to/my/rpc", data);
for (; ; ) // а это уже await { await Task.Delay(200); if (endFlag) break; }
return result; }
а вот уже совсем короткий вариант, что делает по сути то же самое
public Task Test(MyClass data) { var tcs = new TaskCompletionSource(); protocol.Invoke((MyOtherClass response) => // это async { tcs.SetResult(response); }, "uri/to/my/rpc", data); return tcs.Task; }

понедельник, 8 апреля 2019 г.

Многопоточность и ассинхронность

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


Ответ

Прежде всего надо разобраться с терминологией. В самом простом виде программа выполняет последовательно или синхронно, инструкция за инструкцией, в том порядке, в котором они идут в коде.
System.out.println("A"); System.out.println("B"); System.out.println("C");
Каждый вызов println блокирующийся. Это значит, что пока он не закончится, программа не будет выполняться дальше.
В реальном мире от программы требуется реагировать на множество событий, происходящих в произвольные моменты времени, или одновременно выполнить задачи существенно более долгие, чем вывод на экран. Такое выполнение называется многозадачным. Но в этом месте часто возникает путаница, так как вместо этого часто, особенно в мире Java, используют термин "асинхронное выполнение", а у этого термина есть своё значение, более конкретное.
Чтобы лучше разобраться в истинном значении терминов, приведу бытовую аналогию:
Один человек ставит кастрюлю на плиту, ждёт пока она закипит, закидывает пельмени, ждёт пока сварятся, снимает, ставит вторую, ждёт пока закипит... - последовательное однопоточное выполнение. Один человек ставит две кастрюли на плиту, как только одна из них закипела, забрасывает пельмени и т.д. - асинхронное выполнение. Два человека ставят две кастрюли... - многопоточное выполнение.
В данной аналогии плита - это аппаратный ресурс (процессор), пельмени - программный ресурс (сокеты), человек - это поток, а кипение - блокирующийся вызов.
Примечание: К аналогии можно добавить ещё вариант "Два человека ставят две кастрюли на две плиты", описывающий действительную параллельность в многопроцессорной системе. Но с описанием видов многозадачности можно легко выйти за ограничение по количеству символов на ответ.
Очевидно, что при асинхронном выполнении ресурсов требуется меньше, а скорость та же, что и при многопоточном. Именно поэтому проблему c10k могут решать только асинхронные сервера. Минусы в том, что асинхронный код зачастую сложнее и не всё можно выполнять асинхронно.
В вашем прошлом вопросе я уже показывал примеры многопоточного и асинхронного кода:
Многопоточный код
ExecutorService executor = Executors.newFixedThreadPool(10); while (true) { // Вызов accept() блокирующийся и остановит цикл до тех пор // пока не поступит новое соединение SocketChannel socketChannel = serverSocketChannel.accept(); // При этом будет запущен отдельный поток, который будет // обслуживать только это соединение executor.execute(() -> { // Отправка файл, которая займёт 5 секунд например }); // Выполнение продолжится сразу после вызова execute, // меньше чем через миллисекунду. Если подключится ещё один // клиент, для него будет запущен ещё один поток, и будут // передаваться два файла одновременно двум разным клиентам. }
Асинхронный код
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // Цикл событий selector.select(); // Мультиплексирование Set keys = selector.selectedKeys(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next();
if (key.isAcceptable()) { // Поступило новое соединение SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); // Регистрируем его для обработки в этом же потоке // на следующих итерациях цикла socketChannel.register(selector, SelectionKey.OP_READ, new Storage()); }
if (key.isReadable()) { // В одном из соединений появились данные SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(256);
// Читаем столько данных, сколько сейчас есть int read = socketChannel.read(buffer); if (read != -1) { ... } else { // Ничего не прочиталось, значит пора закрывать соединение socketChannel.close(); } }
iterator.remove(); } }
В примере асинхронного кода нет ни одного блокирующегося вызова, цикл никогда не останавливается. Какой вариант многозадачности выбрать - это вопрос имеющий смысл только в контексте конкретной задачи. У каждого варианта свои плюсы и минусы. Из примеров видно, что асинхронного кода требуется больше, но при асинхронном выполнении значительно реже возникает ситуация, когда два человека дерутся за одну пустую кастрюлю, пока в другой догорают пельмени