Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Разработка Windows Forms > Многопоточное приложение |
Автор: SaS1 30.8.2007, 01:06 | ||
Пишу такой код:
ОН компилируется, но во время выполнения выдаёт такую ошибку: Cross-thread operation not valid: Control 'listBox1' accessed from a thread other than the thread it was created on. на строке listBox1.Items.Add(count); И я вот никак не пойму как сделать это правильно. Помогите пожалуйста. |
Автор: Mr_Smith 30.8.2007, 04:39 |
Аксиома многопоточных приложений на .НЕТ, что "контролы можно менять только в контексте породившего их потока" поэтому компилятор на тебя и ругается |
Автор: SpaceSpace 30.8.2007, 07:30 | ||||
Это можно обойти, если делать Invoke делегата функции которая изменяет ресурс в другом потоке во превых, поместим в отдельный метод , который обращается к ресурсу
|
Автор: mihryak 30.8.2007, 11:47 | ||||
с делегатами посимпатичнее выглядит
или (для произвольных сигнатур)
|
Автор: SpaceSpace 31.8.2007, 07:35 | ||
mihryak молодец, повторил то же самое что и я! продолжай в том же духе.
ThreadStart - это и есть делегать на запуск непараметризированного потока |
Автор: mihryak 31.8.2007, 09:13 |
SpaceSpace, обрати внимание, что в первом случае не потребовалось писать дополнительный метод, т.к. делегат EventHandler<EventArgs> определён в MSDN, более того - в твоём коде теряется возможность не только обработать в большинстве случаев ненужные аргументы EventArgs, но и куда более полезный sender. А ведь помимо EventHandler<EventArgs> есть и море других "типовых" generic-обработчиков, где аргументы могуть быть более полезными. Во втором случае вместо дополнительного метода потребовалось только объявить делегат с требуемой сигнатурой, и опять-таки - параметры не теряются, кода меньше, и он выглядит немного понятнее. Да, кроме ThreadStart, есть и ParametrizedThreadStart, но и читаемостью кода та же штука. А сочетание компактности и читаемости - основные признаки хорошего кода, можно ведь и через emit код набросать, но кому это нужно? ![]() ПС. больше оффтопить не буду, ответы автору даны. |
Автор: SpaceSpace 31.8.2007, 09:26 | ||
mihryak Я не считаю это оффтопом. просто объясни
где содержится зерно логики. Да, я согласен, что ты записал код без дополнительного метода, согласись, что модернизировать мое предложение не составит труда - и оно также будет вызываться из обработчика клика по кнопке. Ты считаешь, что это логически и архитектурно обосновано? Как мне кажется, абсолютно нелогично делать BeginInvoke(new EventHandler(button1_Click), sender, e); т.к. это нарушает стройность и логику пиложения, ведь после запуска нужного нам метода может потребоваться и другая логика, именно поэтому Invoke был вынесен за пределы button1_Click, это обосновано и оправдано , жду ваших корректив по этому утверждению На счет того что бывают разнве делегаты и свои и дженереки - с этим никто не спорил, согласен, что для примера логичнее, круче и моднее использовать EventHandler<EventArgs>, |
Автор: mihryak 31.8.2007, 09:54 |
Ок, давай тогда возьмём более реальный пример. Не думаю, что у кого-либо в здравом уме не для тестовых целей возникнет необходимость инвочить обработчик на нажатие кнопки (мышью в другом потоке не кликнешь, а вызывать этот обработчик из другого потока и есть то самое упомянутое нарушение стройности и глупая привязка логики к пользовательскому интерфейсу). Также отбросим вариант, когда потоку просто нужно оповещать форму, что что-то где-то, так что действуй (опять-таки - либо тестовый набросок, либо трэд изменял не-UI свойства формы, и ей нужно только сказать, что эти свойства уже изменились и нужно перестроиться, а это тоже нелучшее решение). Итак, остаётся один из главных вариантов - трэд через эвенты (например) посылает сообщения, содержащие какие-то нужные для бизнес-лигики, а потом и UI данные. В этом случае придётся использовать параметризированный ThreadStart, а у него простое объявление делегата однозначно выигрывает. Таким образом, в обработчике эвента от треда нужно будет обрабатываться только эвент от треда, что к UI не имеет никакого отношения, и не будет уже актуальным, переписывать придётся только трэдовый обработчик. UPD. даже в упрощённой модели я бы не стал вызывать обработчик нажатия мыши по кнопе, вызывая некий аналог AsyncChange, только он скорее всего был бы параметризирован. В нём же бы и инвочил при необходимости. |
Автор: SpaceSpace 31.8.2007, 10:13 | ||||
Вот ты и признал, что твой пример ничем не лучше моего. именно на это я и указывал. ![]() На счет всего остального - соглашусь с тобой. Хочу только заметить, что если инстанс одного (потока) лезет в свойства инстанса другого - т.к. является обработчиком какой либо логики и не имеет к нему отношения, то этот поток, класически является бэкграундным и создан он исключительно для того(не трогаю повышение быстродействия в многопроцессорных системах) чтобы у пользователя появилась иллюзия "одновременности" и "реалтаймовости".
жжешь ![]() Не надо мне предьявлять того, на истинность чего я не посягал. Надо уметь признавать свои ошибки и аргументировать свою точку зрения |
Автор: mihryak 31.8.2007, 10:23 |
резюме такое - нефиг начинать спорить, взяв за основу надуманные или нежизненные примеры, спор выходит ни о чём ![]() ну и напоследок тоже не удержусь от укольчика - в msdn async programming best practice всё-таки при InvoceRequired вместо создания и запуска тредов используют таки инвоки делегатов :-Р |
Автор: SpaceSpace 31.8.2007, 10:26 |
согласен с тобой. приятно пообщаться с умным человеком. ![]() |
Автор: SaS1 1.9.2007, 18:21 |
Ребята, спасибки за ответ. Я попробовала то, что вы предлогали и вроде ошибка не возникала. Но вот какая проблема применительно к моему примеру. Получается что сначала после нажатия бутона прога просто виснет на небольшое время, а потом сразу выводит все циферки и в то время, как она виснет, я не могу по радиобутонам нажимать. Простите за надуманный пример. В идеале у меня должно получиться приложение сервер, которое постоянно ждёт сообщений от клиентов, но при этом пользователь может с ним работать. Вот для этого мне и нужны были несколько потоков:( Так что я до сих пор не понимаю как мне это так хитро сделать. Может поможете? |
Автор: 1stein 1.9.2007, 19:33 |
по-моему вам нужен BackgroundWorker. Вот здесь он на мой взгляд неплохо описан: http://www.rsdn.ru/article/dotnet/WinForms20.xml |
Автор: SpaceSpace 3.9.2007, 07:36 | ||
SaS1, если у тебя будет сервер, то логичнее использовать пул потоков. т.к. при создании нового потока затрачиваются большие резурсы (скажем, 1000 циклов проца), поэтому у тебя
Я думаю, что в твоем случае - это лучший выбор |