[Этюд] Многопоточность
От: 0K Ниоткуда  
Дата: 08.04.11 23:39
Оценка:
Есть форма, которая обращается к удаленному серверу.

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

Конечно, если бы программист знал что такое многопоточность -- он хотя бы использовал BackgroundWorker (самое простое). Но нет, он этого не сделал.

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

Переносить вызов каждого метода в BackgroundWorker.Work, а возвращаемый результат в BackgroundWorker.Complete -- очень долго. Для каждого из 100 длительных вызовов нужно будет создать 2 метода (уже 200) + не запутаться с аргументами и возвращаемыми значениями.

А всего-то нужно чтобы форма не замирала, а бежал ползунок. Остальное без изменений.

Как бы вы сделали?

12.04.11 00:21: Перенесено модератором из '.NET' — TK
Re: [Этюд] Многопоточность
От: vmpire Россия  
Дата: 08.04.11 23:58
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Как бы вы сделали?

Запустить в отдельном потоке саму форму?
Например, через Application.Run()
Re[2]: [Этюд] Многопоточность
От: 0K Ниоткуда  
Дата: 09.04.11 00:16
Оценка:
Здравствуйте, vmpire, Вы писали:

0K>>Как бы вы сделали?

V>Запустить в отдельном потоке саму форму?
V>Например, через Application.Run()

А что от этого изменится? Точно так-же пользователь нажмет на кнопку -- и форма перестанет отвечать. Зависнет.
Re: [Этюд] Многопоточность
От: Cadet  
Дата: 09.04.11 01:28
Оценка:
Здравствуйте, 0K, Вы писали:

[skipped]

0K>Как бы вы сделали?


Application.DoEvents()
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re: [Этюд] Многопоточность
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 09.04.11 07:28
Оценка:
Здравствуйте, 0K, Вы писали:

1. Дождаться выхода C# 5 и добавить в каждый из вызовов ключевое слово async.

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

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

// Some long-running operation
static void Foo1(string s1, string s2)
{
    Console.WriteLine("Foo1({0}, {1})", s1, s2);
    Thread.Sleep(500);
    Console.WriteLine("Foo1 finished");
}

// Calls Action asyncronoulsy
static void InvokeAsync(Action action)
{
    // вызываем action асинхронно каким угодно образом
    Console.WriteLine("Start calling action asyncronously, ThreadId = {0}", 
                Thread.CurrentThread.ManagedThreadId);
    action.BeginInvoke(
        ar=>
        {
            Console.WriteLine("Before calling EndInvoke. ThreadId = {0}",
                    Thread.CurrentThread.ManagedThreadId);
            action.EndInvoke(ar);
            Console.WriteLine("Asyncronous operation finished.");
            // Сюда можно добавить всякие InvokeRequired/Invoke для корректного
            // обновления UI
        }, 
        null);
}

void Main()
{
    string s1 = "s1";
    string s2 = "s2";
    
    Console.WriteLine("Calling Foo1 syncronously");
    Foo1(s1, s2);

    // Достаточно поменять все места обращения к серверу,
    // заменив одну строку на две.
    
    // Calling Foo1 asynchronoulsy
    Action action = () => Foo1(s1, s2);
    Console.WriteLine("Calling foo1 asyncronously");
    InvokeAsync(action);
    Console.WriteLine("Asyncronous call finished");
    
    Thread.Sleep(1000);
}


З.Ы. Наличие в форме сотни обращений к серверу говорит не столько о непонимании того, что такое многопоточность, но и о непонимании того, что такое ООП.
Re[2]: [Этюд] Многопоточность
От: 0K Ниоткуда  
Дата: 09.04.11 08:04
Оценка:
Здравствуйте, Cadet, Вы писали:

C>Application.DoEvents()


Поясните.
Re[3]: [Этюд] Многопоточность
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 09.04.11 08:08
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Здравствуйте, Cadet, Вы писали:


C>>Application.DoEvents()


0K>Поясните.


Если длительная операция состоит из множества мелких этапов, то вызов между этими этапами Application.DoEvents позволит вашему окну перерисоваться. Но если длительная операция производится именно на сервере, а клиент просто ждет, то этот метод работать ИМО не будет.
Re[2]: [Этюд] Многопоточность
От: 0K Ниоткуда  
Дата: 09.04.11 08:09
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>1. Дождаться выхода C# 5 и добавить в каждый из вызовов ключевое слово async.


И что поможет? И GUI будет корректно обновляться из этого метода? А как прогресс-бар запускать?
Re: [Этюд] Многопоточность
От: scale_tone Норвегия https://scale-tone.github.io/
Дата: 09.04.11 08:27
Оценка: +1
Здравствуйте, 0K, Вы писали:

0K>А всего-то нужно чтобы форма не замирала, а бежал ползунок. Остальное без изменений.


Забить на требование, чтобы форма не замирала, и показывать ползунок в отдельном окне в отдельном потоке поверх формы.
Подчеркиваю: просили самое простое решение — получите.
Re[3]: [Этюд] Многопоточность
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 09.04.11 08:27
Оценка: 4 (1)
Здравствуйте, 0K, Вы писали:

0K>Здравствуйте, SergeyT., Вы писали:


ST>>1. Дождаться выхода C# 5 и добавить в каждый из вызовов ключевое слово async.


0K>И что поможет? И GUI будет корректно обновляться из этого метода? А как прогресс-бар запускать?


Да, он будет нормально обновляться из этого метода, поскольку природа асинков такова, что они сразу же привязаны к соответствующему контексту синхронизации, так что сразу после await SomeOperation(), можно будет дергать UI.
Re[2]: [Этюд] Многопоточность
От: 0K Ниоткуда  
Дата: 09.04.11 10:20
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>А что если каждую функцию обращения к серверу привести к одному (максимум двум) типам делегатов, которые уже потом вызвать как угодно асинхронно?


Не получится. Объясняю:


// Some long-running operation
static void Foo1(string s1, string s2)
{
    Console.WriteLine("Foo1({0}, {1})", s1, s2);
    Thread.Sleep(500);
    Console.WriteLine("Foo1 finished");
}

// Calls Action asyncronoulsy
static void InvokeAsync(Action action)
{
    // вызываем action асинхронно каким угодно образом
    Console.WriteLine("Start calling action asyncronously, ThreadId = {0}", 
                Thread.CurrentThread.ManagedThreadId);
    action.BeginInvoke(
        ar=>
        {
            Console.WriteLine("Before calling EndInvoke. ThreadId = {0}",
                    Thread.CurrentThread.ManagedThreadId);
            action.EndInvoke(ar);
            Console.WriteLine("Asyncronous operation finished.");
            // Сюда можно добавить всякие InvokeRequired/Invoke для корректного
            // обновления UI
            //************************************************************************************
            // НЕ сможете добавить InvokeRequired/Invoke -- так как вы не знаете что в форме нужно устанавливать.
            // Это известно лишь в точке вызова (в нашем случае InvokeAsync в функции Main). Однако в Main еще нет данных, которые нужно установить.
            //************************************************************************************
        }, 
        null);
}

void Main()
{
    string s1 = "s1";
    string s2 = "s2";
    
    Console.WriteLine("Calling Foo1 syncronously");
    Foo1(s1, s2);

    // Достаточно поменять все места обращения к серверу,
    // заменив одну строку на две.
    
    // Calling Foo1 asynchronoulsy
    Action action = () => Foo1(s1, s2);
    Console.WriteLine("Calling foo1 asyncronously");
    InvokeAsync(action);
    Console.WriteLine("Asyncronous call finished");
    
    Thread.Sleep(1000);
}


Понятна проблема? То есть ваш код ничем не лучше BacgroudWorker -- и там и у вас нельзя обойтись без дублирования логики. Для каждого вызова нужно создавать 2 метода.
Re[4]: [Этюд] Многопоточность
От: Cadet  
Дата: 09.04.11 10:21
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>Если длительная операция состоит из множества мелких этапов, то вызов между этими этапами Application.DoEvents позволит вашему окну перерисоваться. Но если длительная операция производится именно на сервере, а клиент просто ждет, то этот метод работать ИМО не будет.


Согласен, не дочитал что блокирующий метод единичный. Почему-то решил что блокируется в цикле . В этом случае конечно асинхронность нужна.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re: [Этюд] Многопоточность
От: MxMsk Португалия  
Дата: 12.04.11 06:23
Оценка:
Здравствуйте, 0K, Вы писали:

0K>А всего-то нужно чтобы форма не замирала, а бежал ползунок. Остальное без изменений.

Иные программы вообще не имеют GUI. Очевидно, их написать еще проще

0K>Как бы вы сделали?

Объяснил бы заказчику, что было сделано криво, и переделал с использованием "православных" средств.
Потому что в будущем все-равно придется огребать за костыли.
Re: [Этюд] Многопоточность
От: Vladek Россия Github
Дата: 12.04.11 09:36
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Как бы вы сделали?


Использовал бы всю мощь TPL и контексты синхронизации, работы на 5 минут.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.