Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 12.07.09 18:36
Оценка:
Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).
Как это можно сделать? Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?
Re: Как выполнить метод в другом (основном) потоке?
От: hexamino http://hexamino.blogspot.com/
Дата: 13.07.09 09:32
Оценка:
Здравствуйте, Аноним, Вы писали:

А> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А> Как это можно сделать? Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?

Обычно делается так: вторичный поток складывает делегаты на методы, которые нужно выполнить, в синхронизированную очередь, а основной поток, когда сочтет нужным, сам достает из очереди эти делегаты и выполняет их.
Re: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 10:23
Оценка:
Здравствуйте, Аноним, Вы писали:

А> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А> Как это можно сделать? Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?

Если при выполнении 2 потока 1-й поток находится в ожидании, то он он сможет выполнить другой метод. У вас 2 поток (например) поднимает флаг выполнения чего-то, а первый поток, увидев этот флаг, может начать выполнение этого "чего-то"
Re: Как выполнить метод в другом (основном) потоке?
От: _FRED_ Черногория
Дата: 13.07.09 10:36
Оценка:
Здравствуйте, Аноним, Вы писали:

А> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А> Как это можно сделать?

В "поток 2" можно передать экземпляр SynchronizationContext первого пока. В первом потоке получить его SynchronizationContext можно с помощью SynchronizationContext.Current. Далее во втором потоке вызывай или Post или Send у имеющегося контекста.

А>Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?


А как ответ на этот вопрос влияет на решение задачи?
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 10:53
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>получить его SynchronizationContext можно с помощью SynchronizationContext.Current. Далее во втором потоке вызывай или Post или Send у имеющегося контекста.


поскажите, почему сие может быть null? и как быть?
Re[2]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 11:11
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Аноним, Вы писали:


А>> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А>> Как это можно сделать?

_FR>В "поток 2" можно передать экземпляр SynchronizationContext первого пока. В первом потоке получить его SynchronizationContext можно с помощью SynchronizationContext.Current. Далее во втором потоке вызывай или Post или Send у имеющегося контекста.


А>>Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?


_FR>А как ответ на этот вопрос влияет на решение задачи?


В статье (http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx) прочитал, что можно использовать AsyncOperation и AsyncOperationManager на тот же манер. В статье показан пример, но я не совсем понял, можно ли так делать. В MSDN написано, что под каждую задачу нужно создавать свой AsyncOperation, но при это про SynchronizationContext написано, что он должен быть один на поток. Так как правильно использовать AsyncOperation?
Re: Как выполнить метод в другом (основном) потоке?
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 13.07.09 11:50
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А> Как это можно сделать? Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?

Насколько я понимаю, решение этой задачи возможно только для Windows Forms или WPF приложений. SynchronizationContext.Current будет не null только для этих типов приложений. Если посмотреть документацию, то в ней говорится, что класс SynchronizationContext является абстрактным и имеет два наследника WindowsFormsSynchronizationContext и DispatcherSynchronizationContext, которые соответственно применяются для маршалинга операций в поток пользовательского интерфейса Windows Forms и WPF, соовтетственно.

Классы AsyncOperation и AsyncOperationManager, являются вспомогательными классами при реализации Event-based Asynchronous Pattern, в котором говорится, что событие завершение операции должно выполняться в потоке, начавшем операцию. Примером реализации этого шаблона является класс BackgroundWorker, но даже он не маршалит вызов события завершения операции в исходный поток, при использовании его в консольном приложении.

З.Ы. Книжку Рихтерая читал уже довольно давно, но что-то не припомню вообще возможности в общем случае вызвать некоторый код в контексте произвольного потока (если речь идет не о потоке пользовательского интерфейса).
Re[2]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 12:25
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>Здравствуйте, Аноним, Вы писали:


А>> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А>> Как это можно сделать? Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?

ST>Насколько я понимаю, решение этой задачи возможно только для Windows Forms или WPF приложений. SynchronizationContext.Current будет не null только для этих типов приложений. Если посмотреть документацию, то в ней говорится, что класс SynchronizationContext является абстрактным и имеет два наследника WindowsFormsSynchronizationContext и DispatcherSynchronizationContext, которые соответственно применяются для маршалинга операций в поток пользовательского интерфейса Windows Forms и WPF, соовтетственно.


ST>Классы AsyncOperation и AsyncOperationManager, являются вспомогательными классами при реализации Event-based Asynchronous Pattern, в котором говорится, что событие завершение операции должно выполняться в потоке, начавшем операцию. Примером реализации этого шаблона является класс BackgroundWorker, но даже он не маршалит вызов события завершения операции в исходный поток, при использовании его в консольном приложении.


ST>З.Ы. Книжку Рихтерая читал уже довольно давно, но что-то не припомню вообще возможности в общем случае вызвать некоторый код в контексте произвольного потока (если речь идет не о потоке пользовательского интерфейса).


Да, действительно, проверил. Если AsyncOperation создается в потоке, который является потоком UI, то все работает как описано. Если же этот поток не является потоком UI, то при вызове Post метод по факту выполняется в новом, отдельном потоке.
А в чем принципиальное различие между потоком UI и обычным потоком? Ведь UI-поток — это тоже обычный поток, в котором, как я понимаю, что-то докручено, чтобы работал механизм с AsyncOperation. Можно ли это "что-то" докрутить в произвольный поток?
Re[3]: Как выполнить метод в другом (основном) потоке?
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 13.07.09 12:59
Оценка: 61 (2) +1
Здравствуйте, Аноним, Вы писали:

А> Да, действительно, проверил. Если AsyncOperation создается в потоке, который является потоком UI, то все работает как описано. Если же этот поток не является потоком UI, то при вызове Post метод по факту выполняется в новом, отдельном потоке.

А> А в чем принципиальное различие между потоком UI и обычным потоком? Ведь UI-поток — это тоже обычный поток, в котором, как я понимаю, что-то докручено, чтобы работал механизм с AsyncOperation. Можно ли это "что-то" докрутить в произвольный поток?

Самый простой способ реализовать межпоточное взаимодействие явно с помощью разделяемых переменных и средств синхронизации типа ManualResetEvent, AutoResetEvent или других средств.
По поводу потоков пользовательского интерфейса: поток, работающий с UI нельзя назвать обычным.
Вот, что пишет Рихтер:

Создавая какой-либо поток, система предполагает, что он не будет иметь отношения к поддержке пользовательского интерфейса. Это позволяет уменьшить объем выделяемых ему системных ресурсов. Но, как только поток обратится к той или иной GUI-функции (например, для проверки очереди сообщений или создания окна), система автоматически выделит ему дополнительные ресурсы, необходимые для выполнения задач, связанных с пользовательским интерфейсом А если конкретнее, то система создает структуру THREADINFO и сопоставляет ее с этим потоком.

THREADINFO — это внутренняя (недокументированная) структура, идентифицирующая очередь асинхронных сообщений потока (posted-message queue), очередь синхронных сообщений потока (sent-message queue), очередь ответных сообщений (reply-message queue), оче редь виртуального ввода (virtualized input queue) и флаги пробуждения (wakc flags), она также включает ряд других переменных-членов, характеризующих локальное состояние ввода для данного потока.

Когда с потоком связывается структура THREADINFO, он получает свой набор очередей сообщений. Если процесс создает три потока и все они вызывают функцию Create Window, то и наборов очередей сообщений будет тоже три Сообщения ставятся в очередь асинхронных сообщений вызовом функции PostMessage:

BOOL PostMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

При вызове этой функции система определяет, каким потоком создано окно, идентифицируемое параметром hwnd, Далее система выделяет блок пямяти, сохраняет в нем параметры сообщения и записывает этот блок в очередь асинхронных сообще ний данного потока. Кроме того, функция устанавливает флаг пробуждения QS_POST MESSAGE (о нем — чуть позже). Возврат из PostMessage происходит сразу после того, как сообщение поставлено в очередь, поэтому вызывающий поток остается в неведе нии, обработано ли оно процедурой соответствующего окна На самом деле вполне вероятно, что окно даже не получит это сообщение Такое возможно, если поток, создавший это окно, завершится до того, как обработает все сообщения из своей очереди.


Класс SyncronizationContext (точнее WindowsFormsSynchronizationContext) как раз и использует механизм отправки оконных сообщений для вызова произвольного метода в родном потоке пользовательского интерфейса.
Re[4]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 13:24
Оценка:
решаю похожую задачку, но с небольшими отличиями.

тоже нужно вызвать метод в конкретном потоке, но контроля за этим потоком у меня нет.
в этом потоке выполняется ServiceBase.Run(someServices).

задача следующая: есть несколько сервисов, если какой-нибудь "отваливается", то он должен перевести себя в состояние остановлен. мэнеджт кодом это можно решить посредством вызова Stop самого сервиса, но
class MyService: ServiceBase
{
  // ... здесь запускается поток в котром выполняется вся работа сервиса

  // здесь тело метода выполняющего работу
  void ThreadWork()
  {
    try
    {
    }
    catch (Exception ex)
    {
      // log exception
    }
    // что и как делать здесь чтобы сообщить что сервис остановлен? как я понимаю нужно вызвать Stop(), но в потоке в которому обрабатывается очередь SCM
  }
}
Re[5]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 13:32
Оценка:
Здравствуйте, Аноним, Вы писали:

на данный момент (так как выяснилось, что SynchronizationContext не работает "нормально" с консольными приложениями такой костыль (?):

А>
А>class MyService: ServiceBase
А>{
А>  // ... здесь запускается поток в котром выполняется вся работа сервиса

    void OnStop()
    {
            lock (sync)
            {
                if (!thread.IsAlive)
                    return;
                thread.Abort();
                thread.Join();
            }
    }

А>  // здесь тело метода выполняющего работу
А>  void ThreadWork()
А>  {
А>    try
А>    {
А>    }
А>    catch (Exception ex)
А>    {
А>      // log exception
А>    }
А>    context.Post(delegate { 
                                    thread.Join(); 
                                    Stop(); 
                                  }, null);
А>  }
А>}
А>


оцените плиз!
Re[6]: Как выполнить метод в другом (основном) потоке?
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 13.07.09 13:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Аноним, Вы писали:


А>на данный момент (так как выяснилось, что SynchronizationContext не работает "нормально" с консольными приложениями такой костыль (?):


А>>
А>>class MyService: ServiceBase
А>>{
А>>  // ... здесь запускается поток в котром выполняется вся работа сервиса

А>    void OnStop()
А>    {
А>            lock (sync)
А>            {
А>                if (!thread.IsAlive)
А>                    return;
А>                thread.Abort();
А>                thread.Join();
А>            }
А>    }

А>>  // здесь тело метода выполняющего работу
А>>  void ThreadWork()
А>>  {
А>>    try
А>>    {
А>>    }
А>>    catch (Exception ex)
А>>    {
А>>      // log exception
А>>    }
А>>    context.Post(delegate { 
А>                                    thread.Join(); 
А>                                    Stop(); 
А>                                  }, null);
А>>  }
А>>}
А>>


А>оцените плиз!


Ну, тут как минимум серьезный вопрос к архитектуре.
Во-первых, есть понятие активной сущности (см. Мейера), т.е. сущности, которая может сама в произвольный момент времени выполнять некоторые операции. Обычно то, что сущность является активной может быть скрыто от посторонних глаз.
Т.е. у тебя есть disposable класс MyService, который и выполняет всю работу. Не нужно для этого явно создавать поток в коде службы. Как минимум, таким способом ты облегчишь написание служб. У меня всегда есть бизнесс-сущности, которые я могу использовать как в консольном приложении (для простоты отладки) так и в сервисе. Процесс отладки запуска служб существует, но зачем идти так сложно? Закон один класс — одна задача, еще никто не отменял. А у тебя один класс (класс службы) занимается и проблемами потоков и проблемами служб и, видимо, кучей других проблем.

Во-вторых, когда твой класс MyService понял, что он не может больше выполнять свою работу, почему он не может попытаться восстановить свое состояние самостоятельно? Если же он сделать по какой-то причине он этого не может то тут тоже есть несколько способов решения этой проблемы. Если в сервисе произошла ошибка, после которой он не может восстановить свою работу, так и не обрабатывайте эту ошибку. Пусть ошибка будет необработана и CLR сама закроет ваш процесс, с записью в журнал события о необработанной ошибке. Если и этот вариант не подходит, то лучше у класса MyService сделать какое-то событие, типа ServiceFailed (или что-то в этом духе), которое говорит о том, что сервис таки сдох. И теперь уже на уровне службы прибивай этот процесс одним из следующих способов (наверняка есть еще куча других):
ServiceController.Stop() — посылаем SCM сообщение с желанием остановки службы
Environment.Exit(), Environment.FailFast(), Process.GetCurrentProcess().Close() — прибиваем процесс разными способами.

З.Ы. Способ с самоостановкой все же какой-то кривой. Я не разу не видел службы которая бы принудительно себя бы прибивала. Возникла необрабатываемая ошибки — не обрабатывай ее и процесс будет прибит с записью в журнал событий и всеми вытекающими.
Re: Как выполнить метод в другом (основном) потоке?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.07.09 14:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А> Есть основной поток (поток 1), который выполняет работу. Асинхронно приходят сообщения. По сути дергается метод в другом потоке (поток 2) и необходимо вызвать определенный метод в основном потоке (потоке 1).

А> Как это можно сделать? Когда будет вызван метод в потоке 1? Если в этот момент в потоке 1 будет выполнятся какой-то другой метод, его исполнение приостановится, или метод выполнится после того как текущий метод в потоке 1 завершит свою работу?
Так или иначе, методы вызываемые в друго потоке имеют очередь
1. Оконная очередь для Окна
2. Очередь для потока.
3. APC очередь для QueueUserAPC (см http://rsdn.ru/forum/dotnet/616700.flat.aspx#616700
Автор: TK
Дата: 22.04.04
) которая выполняется в том числе и при SleepEx.
Можно самому организовать очередь и проверять ее по мере возможности.
и солнце б утром не вставало, когда бы не было меня
Re[7]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 15:03
Оценка:
Здравствуйте, SergeyT., Вы писали:

спасибо что откликнулись!

ST>Ну, тут как минимум серьезный вопрос к архитектуре...

ну эт я привел такой дурацкий пример. на самом деле у меня есть отдельная сущность "служба", которая реализует некоторый контракт (Start, Stop, etc), а дергать ее может как консольное приложение (если запущено например с параметром -console) либо сам SCM, где создаются экземпляры ServiceBase которые дергают описанную сущность.

ST>Во-вторых, когда твой класс MyService понял, что он не может больше выполнять свою работу, почему он не может попытаться восстановить свое состояние самостоятельно? И теперь уже на уровне службы прибивай этот процесс одним из следующих способов (наверняка есть еще куча других):

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

ST>ServiceController.Stop() — посылаем SCM сообщение с желанием остановки службы

тут не соглашусь, потому как WinAPI представляет интерфейс для установки состояния службы (собственно на этом строится процесс перехода от StopPending до Stopped, например). кроме того сама служба предоставляет Stop который собственно и выполняет сию работу (т.е. просто устанавливает статус Stopped) — значит предполагает остановку службы "руками".


вот и подумалось мне, что сущность-служба должна сообщить своему "хостеру", что остановилась (не работает), логичным показалось использовать hostService.Stop.

хотелось бы обсудить такую архитектуру (multi-service hosting), ничего на эту тему не нашел. более того после плотной работы (и просмотра reflector'ом) с C# реализацией (которая мне показалась очень нищей) сложилось впечатление чего-то нефункционального в полной мере
Re[8]: Как выполнить метод в другом (основном) потоке?
От: AK107  
Дата: 13.07.09 15:43
Оценка:
Здравствуйте, Аноним, Вы писали:

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


и еще пока нет мыслей как сделать мультидоменную архитектуру: один сервис — один домен приложений — опять таки проблемы с остновкой сервисов. в том виде что я сделал по идее раьотать должно, но еще более коряво выглядит

такое ощущение что все сложности вырастают от непродуманной реализации сервисов в .net
Re[9]: Как выполнить метод в другом (основном) потоке?
От: SergeyT. США http://sergeyteplyakov.blogspot.com/
Дата: 13.07.09 17:38
Оценка:
Здравствуйте, AK107, Вы писали:

AK>и еще пока нет мыслей как сделать мультидоменную архитектуру: один сервис — один домен приложений — опять таки проблемы с остновкой сервисов. в том виде что я сделал по идее раьотать должно, но еще более коряво выглядит


AK>такое ощущение что все сложности вырастают от непродуманной реализации сервисов в .net


Ну, сервисы же не в .net реализованы . Это ведь всего лишь обертка над Win API, которое о .net слыхом не слыхивала. А чем не подходит стандартный средства сервисов, которые предусматривают хостинг более чем одного сервиса в одном исполняемом файле?
Re[10]: Как выполнить метод в другом (основном) потоке?
От: Аноним  
Дата: 13.07.09 18:46
Оценка:
Здравствуйте, SergeyT., Вы писали:

ST>Ну, сервисы же не в .net реализованы . Это ведь всего лишь обертка над Win API, которое о .net слыхом не слыхивала. А чем не подходит стандартный средства сервисов, которые предусматривают хостинг более чем одного сервиса в одном исполняемом файле?


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

(кстати очень напоминает принцип oracle jobs — работают независимо и не влияют на работу друг-друга даже в случаях ошибок)

в идеале хочу плагинный подход но с доменами приложений под каждую сущность-службу. при старте сервиса создается домен, запускается служба, при остановке — стопим, выгружаем домен. да тут возникла трабла с тем, что сервис может отвалится: ну вот конкретный пример — сервис работает под учеткой, происодит имперсонация и... ошибка пароль не подходит. ИМХО, нужно писать в лог сервиса об этой конкретной ошибке, в журнал событий приложения fatal error, и, переводить службу с состояние Stopped. Вот с последним и бьюсь

кроме того в .net есть косяк с выгрузкой сборок (т.е. их и выгрузить нельзя), а было бы неплохо, например для обновления плагина при работающем процессе. AppDomain.Unload не помогает потому как тип сборки-плагина загружены получается и в главный домен тоже (ну чтобы иметь типы из которых потом будут создаваться инстансы служб)

короче дурдом, куда не копни — везде какие-то косяки архитектуры
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.