Есть форма, которая обращается к удаленному серверу.
Программист, который писал программу, видимо ничего не знал о многопоточности. Все операции выполняются в одном потоке и во время длительных операций форма перестает реагировать на обращения (замирает).
Конечно, если бы программист знал что такое многопоточность -- он хотя бы использовал BackgroundWorker (самое простое). Но нет, он этого не сделал.
В связи с этим родился этюд. Как проще и быстрее всего переделать проект, чтобы форма не замирала при выполнении длительной операции?
Переносить вызов каждого метода в BackgroundWorker.Work, а возвращаемый результат в BackgroundWorker.Complete -- очень долго. Для каждого из 100 длительных вызовов нужно будет создать 2 метода (уже 200) + не запутаться с аргументами и возвращаемыми значениями.
А всего-то нужно чтобы форма не замирала, а бежал ползунок. Остальное без изменений.
Как бы вы сделали?
12.04.11 00:21: Перенесено модератором из '.NET' — TK
1. Дождаться выхода C# 5 и добавить в каждый из вызовов ключевое слово async.
Если первый вариант не подходит ибо долго ждать, то переходим к другому (набросал на коленках, так что хз, подойдет или нет).
А что если каждую функцию обращения к серверу привести к одному (максимум двум) типам делегатов, которые уже потом вызвать как угодно асинхронно?
// Some long-running operationstatic void Foo1(string s1, string s2)
{
Console.WriteLine("Foo1({0}, {1})", s1, s2);
Thread.Sleep(500);
Console.WriteLine("Foo1 finished");
}
// Calls Action asyncronoulsystatic 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);
}
З.Ы. Наличие в форме сотни обращений к серверу говорит не столько о непонимании того, что такое многопоточность, но и о непонимании того, что такое ООП.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, Cadet, Вы писали:
C>>Application.DoEvents()
0K>Поясните.
Если длительная операция состоит из множества мелких этапов, то вызов между этими этапами Application.DoEvents позволит вашему окну перерисоваться. Но если длительная операция производится именно на сервере, а клиент просто ждет, то этот метод работать ИМО не будет.
Здравствуйте, 0K, Вы писали:
0K>А всего-то нужно чтобы форма не замирала, а бежал ползунок. Остальное без изменений.
Забить на требование, чтобы форма не замирала, и показывать ползунок в отдельном окне в отдельном потоке поверх формы.
Подчеркиваю: просили самое простое решение — получите.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, SergeyT., Вы писали:
ST>>1. Дождаться выхода C# 5 и добавить в каждый из вызовов ключевое слово async.
0K>И что поможет? И GUI будет корректно обновляться из этого метода? А как прогресс-бар запускать?
Да, он будет нормально обновляться из этого метода, поскольку природа асинков такова, что они сразу же привязаны к соответствующему контексту синхронизации, так что сразу после await SomeOperation(), можно будет дергать UI.
Здравствуйте, SergeyT., Вы писали:
ST>А что если каждую функцию обращения к серверу привести к одному (максимум двум) типам делегатов, которые уже потом вызвать как угодно асинхронно?
Не получится. Объясняю:
// Some long-running operationstatic void Foo1(string s1, string s2)
{
Console.WriteLine("Foo1({0}, {1})", s1, s2);
Thread.Sleep(500);
Console.WriteLine("Foo1 finished");
}
// Calls Action asyncronoulsystatic 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 метода.
Здравствуйте, SergeyT., Вы писали:
ST>Если длительная операция состоит из множества мелких этапов, то вызов между этими этапами Application.DoEvents позволит вашему окну перерисоваться. Но если длительная операция производится именно на сервере, а клиент просто ждет, то этот метод работать ИМО не будет.
Согласен, не дочитал что блокирующий метод единичный. Почему-то решил что блокируется в цикле . В этом случае конечно асинхронность нужна.
Здравствуйте, 0K, Вы писали:
0K>А всего-то нужно чтобы форма не замирала, а бежал ползунок. Остальное без изменений.
Иные программы вообще не имеют GUI. Очевидно, их написать еще проще
0K>Как бы вы сделали?
Объяснил бы заказчику, что было сделано криво, и переделал с использованием "православных" средств.
Потому что в будущем все-равно придется огребать за костыли.