Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1

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

07.04.2019, 20:13. Показов 1553. Ответов 4
Метки нет (Все метки)

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

Есть программа с UI, которая отображает список сущностей, где сущность - это одна такая папка со всем её содержимым. Сущность умеет отображать своё текущее состояние: Ожидает расчётов, расчёты в процессе, Расчёты приостановлены (пауза), расчёты завершены (заново уже запустить нельзя), "Сущность в процессе удаления" (иногда это значит, что она просто ждёт своей очереди, пока до неё дойдёт очередь).

Пользователь может выбрав в списке сущность нажать по ней и выбрать действие "Включить расчёты", "Пауза" (если расчёты были ранее запущены). Так же он может включить одновременно несколько, но в один момент времени может работать только одни такой расчёт.

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

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

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

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


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


Вопрос:
Я пришёл к выводу, что такое писать в синхронном стиле с использованием async await не самая лучшая идея, ибо очень тяжело дебажить. Нужно чёткое разделение и очерёдность выполнения. Возможно, отправка сообщений куда-то и постановка в очередь на выполнение. Это я теперь так вижу, когда уже поздно переписывать .

UI у меня UWP + паттерн MVVM.

Может быть есть у кого-то мысли, как правильно организовываются такие приложения? Есть какие-то книги или статьи, на которые стоит обратить внимание?

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

Параллельную работу над сущностью (данными внутри папки) я могу контролировать словарём, где ключ - это ID сущности, а значение это структура данных с TaskCompletionSource внутри. Поток 1 заходит в метод и читает из словаря по ID сущности данные, если они там есть, то он получает Task (await waitForCompletionTask), который завершает свою работу, когда Поток 2 освободит блокировку (TaskCompletionSource.SetResult(null)). Блокировка освобождается, то другой поток берёт блокировку и начинает работать. Вроде более-менее удобно, если метод один. А если это разные методы... В общем, всё это как-то неудобно.


Интересует, как правильно можно организовать работу и взаимодействие потоков в такой программе. Отправлять данные в UI (у меня не везде есть ссылки на ViewModel, где нет есть события, но всё это пораждает огромное кол-во кода в виде подписок на события, отписок).

Могу набросать пример такого проекта со всеми подходами, что я использую, но это займёт немало времени и не факт, что это вообще нужно, если есть опыт и есть что посоветовать, я бы почитал об этом.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
07.04.2019, 20:13
Ответы с готовыми решениями:

Хочу перейти с консолей на приложения винды: в какую сторону копать?
хочу перейти с консолей на приложения винды... не подскажете с чего начать что почитать? п.с. цель написать крестики нолики онлайн :)

Работа с последовательностями (в какую сторону копать)
Задачка реально элементарная, но я ее не могу решить уже который день Есть таблица со значениями: Digit 1 2 4 5 6 7 10

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

4
Эксперт .NET
 Аватар для Usaga
14136 / 9359 / 1350
Регистрация: 21.01.2016
Сообщений: 35,174
08.04.2019, 03:49
Casper-SC, довольно обще описано. Не понятно, чего вам можно советовать.

А вот TaskCompletionSource использовать описанным образом явно не стоит. Криво как-то выглядит, да и не по назначению оно используется. Тут больше подходит ManualResetEventSlim.

Цитата Сообщение от Casper-SC Посмотреть сообщение
такое писать в синхронном стиле с использованием async await не самая лучшая идея
async await как раз про асинхронный стиль.
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
08.04.2019, 11:14  [ТС]
Usaga, говоря про синхронный стиль я имею ввиду, что код отливается от синхронного только наличием async await. То есть, как будто код и не асинхронный на первый взгляд со стороны. Я про это.

Ладно, я попробую набросать проект имитацию, и скину сюда, можно будет обсудить, какой лучше подход использовать в моём случае. Но это будет не в ближайшие пару дней, но постараюсь как можно раньше.
0
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
09.04.2019, 19:22
Цитата Сообщение от Casper-SC Посмотреть сообщение
Сущности не могут удаляться моментально, часто это значительное время, так как файлы большие.
Любое обращение к файловой системе можно считать длительной операцией.
Цитата Сообщение от Casper-SC Посмотреть сообщение
Программу могут закрыть в любой момент и не факт, что все запланированные расчёты или удаления выполнятся.
В общем случае пользователь может открыть вашей прогой папку, где будет порно по категориям. Какое поведение программы при этом приемлемо, решать вам: отобразить все папки как валидные сущности и удалять их по мере проверки / возникновения ошибок обработки, или не показывать непроверенные папки вовсе.
Цитата Сообщение от Casper-SC Посмотреть сообщение
Возможно, отправка сообщений куда-то и постановка в очередь на выполнение.
С каждой сущностью всегда ассоциирована задача (либо рабочая, либо выполненная). Следующую задачу можно запланировать с помощью task.ContinueWith. Тем более, что этот метод имеет перегрузку, принимающую TaskScheduler:
C#
1
_associatedTask = _associatedTask.ContinueWith(t => _entity.Process(), processingTaskScheduler);
Вы можете таким образом одновременно назначить 10 задач обработки для разных сущностей, но выполняться они будут последовательно, ибо вы напишите свой TaskScheduler, который таргетит один-единственный поток.
Цитата Сообщение от Casper-SC Посмотреть сообщение
Может быть есть у кого-то мысли, как правильно организовываются такие приложения? Есть какие-то книги или статьи, на которые стоит обратить внимание?
Есть альтернатива: программирование с явным выделением состояний. Вам нужно нарисовать диаграмму состояний, в которых может находиться сущность, а затем, по этой диаграмме реализовать конечный автомат. Важно выделить состояния таким образом, чтобы любые валидные транзиции между ними осуществлялись мгновенно. Если вы не можете мгновенно создать сущность, так как это ведет к обращению к файловой системе, то это означает, что у сущности есть не только состояние Created, но и промежуточное состояние Creating, например.

Ну допустим реализована сущность, которая как-то работает с файловой системой.
C#
1
2
3
4
5
6
7
8
class Entity
{
    public void Create() { }
    public void Delete() {}
    public void Process() {}
    public void Pause() {}
    public void Stop() {}
}
Выделим состояния, в которых она может находиться. А также события (по типу "звпрошено удаление", "обработка закончена" и т.д.).
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
enum EntityState : int
{
    Creating = 0,
    Created,
    Deleting,
    Deleted,
    Processing,
    Processed,
    Pausing,
    Paused,
    Stopping,
    Stopped
}
 
enum EntityEvent : int
{
    Created = 0,
    CreationError,
    Process,
    Processed,
    ProcessingError,
    Pause,
    Paused,
    Stop,
    Stopped,
    Delete,
    Deleted,
    DeletionError,
}
Теперь к сущности присоединим инфу, необходимую для синхронизации потоков и прочего. Примерно так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class EntitySync
{
    private Automaton<EntitySync, EntityState, EntityEvent> Automaton { get; }
    
    private Task _associatedTask;
    private readonly TaskScheduler ProcessingTaskScheduler;
    
    public object SyncRoot { get; } = new object();
    public Entity Entity { get; }
    
    public EntityState State
    {
        get
        {
            lock (SyncRoot)
            {
                return Automaton.State;
            }
        }
        private set
        {
            lock (SyncRoot)
            {
                Automaton.State = value;
            }
        }
    }
    
    public EntitySync(Entity entity, TaskScheduler processingTaskScheduler)
    {
        Entity = entity;
        ProcessingTaskScheduler = processingTaskScheduler;
        Automaton = new Automaton<EntitySync, EntityState, EntityEvent>(this, EntityState.Creating);
        
        BuildAutomaton();
        
        _associatedTask = Task.Factory.StartNew(() =>
        {
            Do(Entity.Create, EntityEvent.CreationError, EntityEvent.Created);
        });
    }
    
    public void SendEvent(EntityEvent e)
    {
        lock (SyncRoot)
        {
            Automaton.SendEvent(e);
        }
    }
 
    private void Do(Action action, EntityEvent failEvent, EntityEvent successEvent)
    {
        try
        {
            action();
        }
        catch
        {
            SendEvent(failEvent);
            return;
        }
        
        SendEvent(successEvent);
    }
}
Ну и теперь по диаграмме состояний надо будет запрограммировать управляющий автомат. Например тот факт, что сущность может быть удалена во время обработки, можно отразить как-то так.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    private void BuildAutomaton()
    {
        Action<object> noAction = delegate {};
        
        Automaton.DefaultAction = e =>
        {
            throw new Exception($"Error: state is {e.State}, event is {e.Automaton.LastEvent}");    
        };
        
        Automaton[EntityState.Processing, EntityEvent.Delete] = e =>
        {
            lock (SyncRoot)
            {
                Entity.Stop();
                _associatedTask = _associatedTask.ContinueWith(t => Do(Entity.Delete, EntityEvent.DeletionError, EntityEvent.Deleted));
                State = EntityState.Deleting;
            }   
        };
        
        Automaton[EntityState.Deleting, EntityEvent.Processed] = noAction;
        Automaton[EntityState.Deleting, EntityEvent.ProcessingError] = noAction;
        
        // ...
    }
Ну и, разумеется, EntitySync может генерить события типа "состояние изменилось" или "прогресс текущей операции изменился", чтобы уведомлять модель, содержащую весь список сущностей, которая уже будет генерировать события вовне.

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

Добавлено через 36 минут
Хотя с методами Stop / Pause я погорячился. Сложно будет гарантировать вызов метода Stop после метода Process. Так что будет лучше, если Entity научится c CancellationToken работать.
1
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
09.04.2019, 23:09  [ТС]
TopLayer, спасибо. Интересно. Попробую что-то набросать рабочее. Мысль примерно ясна.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
09.04.2019, 23:09
Помогаю со студенческими работами здесь

Скрипт для телеграмма. В какую сторону копать
Хотелось бы написать программу на java, который бы мониторил изменения на google drive и отправлял бы инфу об изменениях в указанный чат...

Архитектура многопоточного приложения
Всем привет. Есть объект, выполняющий ГУИ билдинг. Для этого необходимо загрузить данные из файла и далее записать(для...

Архитектура многопоточного приложения
Добрый вечер! Подскажите правильно ли я использую критическую секцию: Вне всех форм и классов объявляю TCriticalSection *p_cs = new...

Архитектура многопоточного приложения
Всем доброго времени суток. На даннй момент есть следующая задача: реализовать winForm приложение на основе потоков: в первом потоке...

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
Ответ Создать тему
Новые блоги и статьи
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru