Добрый день.
Есть приложение в котором надо организовать два процесса (или потока): один основной, второй дополнительный. Дополнительный поток не ограничивается выполнением одной операции. Он должен "жить" на протяжении всей работы приложения. Он должен при обработке одного из своих внутренних событий: сигнализировать основному потоку о произошедшем событии и приостановить свою работу пока не будет получен ответ от основного потока. С приостановками работы все понятно (через WaitEventHandler). Вопрос с передачей управления между потоками. Вообщем вопрос: как можно из дополнительного потока передать сигнал в основной поток, так чтобы следующие действия выполнялись именно в основном потоке?
Здравствуйте, Roman039, Вы писали:
R>Добрый день. R>Есть приложение в котором надо организовать два процесса (или потока): один основной, второй дополнительный. Дополнительный поток не ограничивается выполнением одной операции. Он должен "жить" на протяжении всей работы приложения. Он должен при обработке одного из своих внутренних событий: сигнализировать основному потоку о произошедшем событии и приостановить свою работу пока не будет получен ответ от основного потока. С приостановками работы все понятно (через WaitEventHandler). Вопрос с передачей управления между потоками. Вообщем вопрос: как можно из дополнительного потока передать сигнал в основной поток, так чтобы следующие действия выполнялись именно в основном потоке?
двойной AutoResetEvent, один в основном потоке, другой в дополнительном. Подробнее в статье
Здравствуйте, Roman039, Вы писали:
R>Добрый день. R>Есть приложение в котором надо организовать два процесса (или потока): один основной, второй дополнительный. Дополнительный поток не ограничивается выполнением одной операции. Он должен "жить" на протяжении всей работы приложения. Он должен при обработке одного из своих внутренних событий: сигнализировать основному потоку о произошедшем событии и приостановить свою работу пока не будет получен ответ от основного потока. С приостановками работы все понятно (через WaitEventHandler). Вопрос с передачей управления между потоками. Вообщем вопрос: как можно из дополнительного потока передать сигнал в основной поток, так чтобы следующие действия выполнялись именно в основном потоке?
Использовать очередь (producer/consumer).
Основной поток это UI поток?
Здравствуйте, Roman039, Вы писали:
R>Дополнительный поток не ограничивается выполнением одной операции. Он должен "жить" на протяжении всей работы приложения.
Зачем?
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, Roman039, Вы писали:
R>>Дополнительный поток не ограничивается выполнением одной операции. Он должен "жить" на протяжении всей работы приложения. HL>Зачем?
Он является постоянной составляющей работы всего приложения.
Этот поток должен контролировать работу внешнего компонента. Если во внешнем компоненте возникает "вопрос" поток должен сигнализировать основному потоку и дождаться ответа на этот "вопрос" (при этом надо приостановить работу внешнего компонента).
Здравствуйте, Roman039, Вы писали:
R>Этот поток должен контролировать работу внешнего компонента. Если во внешнем компоненте возникает "вопрос" поток должен сигнализировать основному потоку и дождаться ответа на этот "вопрос" (при этом надо приостановить работу внешнего компонента).
Тема жизни потока на протяжении всей работы приложения не раскрыта. Ожидать возникновения "вопроса" во внешнем компоненте можно по событию, после чего брать поток из пула. Иначе получится поток, который бОльшую часть времени будет тупо спать.
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, Roman039, Вы писали:
R>>Этот поток должен контролировать работу внешнего компонента. Если во внешнем компоненте возникает "вопрос" поток должен сигнализировать основному потоку и дождаться ответа на этот "вопрос" (при этом надо приостановить работу внешнего компонента). HL>Тема жизни потока на протяжении всей работы приложения не раскрыта. Ожидать возникновения "вопроса" во внешнем компоненте можно по событию, после чего брать поток из пула. Иначе получится поток, который бОльшую часть времени будет тупо спать.
Ожидание "вопроса" и происходит по событию. Сложность в том чтобы остановить выполнение работы компонента до момента получения ответа (а ответ придет из UI). Поток бОльшую часть времени спать не будет, потому что все приложение это своеобразная "оболочка" для этого компонента.
Здравствуйте, Roman039, Вы писали:
R>Сложность в том чтобы остановить выполнение работы компонента до момента получения ответа (а ответ придет из UI).
Какое отношение остановка работы компонента имеет к многопоточности?
Если компонент предоставляет событие (в смысле C# event, а не примитив синхронизации), то, если при наступлении события обработчики вызываются компонентом синхронно, никакой проблемы нет и необходимости в дополнительном протоке нет, т.к. возможны два варианта:
1) компонент генерирует события в UI-потоке; ваш обработчик вызовется в UI-потоке, следовательно, компонент будет ждать, пока обработчик не завершит работу;
2) компонент генерирует события в фоновом потоке; ваш обработчик вызовется в фоновом потоке, следовательно, вы используете в обработчике нужный котекст синхронизации, который остановит фоновый поток, пока не выполнится ваш код из UI-потока.
Если компонент при наступлении события вызывает обработчики асинхронно (что вряд ли), то без каких-либо дополнительных средств, предоставляемых компонентом для остановки его работы, вы эту задачу не решите. И не важно при этом, сколько будет у вас потоков.
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, Roman039, Вы писали:
R>>Сложность в том чтобы остановить выполнение работы компонента до момента получения ответа (а ответ придет из UI). HL>Какое отношение остановка работы компонента имеет к многопоточности?
HL>Если компонент предоставляет событие (в смысле C# event, а не примитив синхронизации), то, если при наступлении события обработчики вызываются компонентом синхронно, никакой проблемы нет и необходимости в дополнительном протоке нет, т.к. возможны два варианта: HL>1) компонент генерирует события в UI-потоке; ваш обработчик вызовется в UI-потоке, следовательно, компонент будет ждать, пока обработчик не завершит работу; HL>2) компонент генерирует события в фоновом потоке; ваш обработчик вызовется в фоновом потоке, следовательно, вы используете в обработчике нужный котекст синхронизации, который остановит фоновый поток, пока не выполнится ваш код из UI-потока.
HL>Если компонент при наступлении события вызывает обработчики асинхронно (что вряд ли), то без каких-либо дополнительных средств, предоставляемых компонентом для остановки его работы, вы эту задачу не решите. И не важно при этом, сколько будет у вас потоков.
Да, компонент генерирует событие синхронно. Я тут пришел к выводу (НЕ без помощи ответов на мой вопрос), что два потока обязательны, но они не обязательно должны работать одновременно. Так что мне вполне подходит решение с переключением конекста рабочего потока (с использованием двух экземпляров EventWaitHandler и я так понял это 2-й предложенный вариант).
Вообщем спасибо за помощь!
P.S. Первый вариант к сожалению не подходит, потому что в UI-потоке в ответ на событие будет сформирована форма вопроса для пользователя и на этом формально обработка события будет закончена, но ответа на вопрос не получено.
Здравствуйте, Roman039, Вы писали:
R>Здравствуйте, achmed, Вы писали:
A>>Использовать очередь (producer/consumer). A>>Основной поток это UI поток?
R>Можно поподробнее или ссылочку.
Здравствуйте, achmed, Вы писали:
A>Здравствуйте, Roman039, Вы писали:
R>>Здравствуйте, achmed, Вы писали:
A>>>Использовать очередь (producer/consumer). A>>>Основной поток это UI поток?
R>>Можно поподробнее или ссылочку.
A>Например здесь
Здравствуйте, Roman039, Вы писали: R>с использованием двух экземпляров EventWaitHandler и я так понял это 2-й предложенный вариант
Нет.
Видимо, у меня не получается понятно объяснить.
Вот тестовый "компонент", имитирующий асинхронное получение каких-то данных:
Скрытый текст
internal sealed class DataArrivedEventArgs : EventArgs
{
private readonly Int32 data;
public DataArrivedEventArgs(Int32 data)
{
this.data = data;
}
public Int32 Data
{
get { return data; }
}
}
internal sealed class MyComponent
{
private const Int32 dueTime = 5000;
private readonly Timer timer;
private readonly Random random;
public MyComponent()
{
this.timer = new Timer(TimerElapsed, null, dueTime, Timeout.Infinite);
this.random = new Random();
}
private void TimerElapsed(Object state)
{
OnDataArrived(new DataArrivedEventArgs(random.Next()));
}
private void OnDataArrived(DataArrivedEventArgs args)
{
var handler = DataArrived;
if (handler != null)
{
DataArrived(this, args);
}
timer.Change(dueTime, Timeout.Infinite);
}
public event EventHandler<DataArrivedEventArgs> DataArrived;
}
Когда доступны данные, компонент генерирует событие. Обработчики события вызываются компонентом синхронно, но в рабочем потоке из пула.
По условию задачи, нужно остановить работу компонента, оповестить UI-поток и дождаться ответа из UI-потока.
Вот код, который выполняет заданную работу:
Скрытый текст
public partial class Form1 : Form
{
private readonly MyComponent component;
private readonly SynchronizationContext context;
public Form1()
{
InitializeComponent();
this.context = SynchronizationContext.Current;
this.component = new MyComponent();
this.component.DataArrived += new EventHandler<DataArrivedEventArgs>(DataArrived);
}
private void DataArrived(Object sender, DataArrivedEventArgs args)
{
context.Send(_ => textBox1.Text = args.Data.ToString(), null);
}
}
Выделенное полужирным можно заменить чем угодно. Это "что угодно" обладает следующими характеристиками:
— оно выполнится в UI-потоке (UI-поток оповещен и выполняет какой-то код);
— пока оно не выполнится, рабочий поток, из которого было сгенерировано событие, будет остановлен (DataArrived будет вызван из рабочего потока, но остановится на context.Send и будет ждать его завершения).
Где вы тут собираетесь использовать два потока и "два экземпляра EventWaitHandler" —
Либо у вас какой-то экзотический "компонент", о котором вы что-то не договариаете.
HL>Выделенное полужирным можно заменить чем угодно. Это "что угодно" обладает следующими характеристиками: HL>- оно выполнится в UI-потоке (UI-поток оповещен и выполняет какой-то код); HL>- пока оно не выполнится, рабочий поток, из которого было сгенерировано событие, будет остановлен (DataArrived будет вызван из рабочего потока, но остановится на context.Send и будет ждать его завершения).
HL>Где вы тут собираетесь использовать два потока и "два экземпляра EventWaitHandler" — HL>Либо у вас какой-то экзотический "компонент", о котором вы что-то не договариаете.
Подскажите, а такой манёвр (переключение контекста синхронизации) возможен только при использовании UI-потока? Вообщем если инициализировать контекст в форме, то все работает как требуется. А если попробовать реализовать подобную схему с обычными (не UI) классами, то значение SinhronizationContext.Current сбрасывается в null и фактического переключения не происходит. Можно переключать контекст не на UI-поток?
Здравствуйте, Roman039, Вы писали:
R>Подскажите, а такой манёвр (переключение контекста синхронизации) возможен только при использовании UI-потока?
Реализации контекста синхронизации из коробки, AFAIK, только для UI (WinForms/WPF/SL).
А как и зачем вы собираетесь использовать контекст синхронизации для не-UI-потоков?
R>Вообщем если инициализировать контекст в форме, то все работает как требуется.
Потому что инфраструктура WinForms инициализирует контекст UI-потока экземпляром WindowsFormsSynchronizationContext.
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, Roman039, Вы писали:
R>>Подскажите, а такой манёвр (переключение контекста синхронизации) возможен только при использовании UI-потока? HL>Реализации контекста синхронизации из коробки, AFAIK, только для UI (WinForms/WPF/SL). HL>А как и зачем вы собираетесь использовать контекст синхронизации для не-UI-потоков?
Я думал может таким образом можно вообще управлять тем, в каком потоке будет исполняться операция. Просто обычно (у меня) при разработке многопоточных приложений не всегда очевидно в каком потоке выполняется та или иная функция, это ведет к проблемам отладки и возникает ощущение хаоса в приложении. Хотел найти способ упорядочить выполнение многопоточных приложений.
R>>Вообщем если инициализировать контекст в форме, то все работает как требуется. HL>Потому что инфраструктура WinForms инициализирует контекст UI-потока экземпляром WindowsFormsSynchronizationContext.
Здравствуйте, Roman039, Вы писали:
R>Вопрос с передачей управления между потоками.
Давай я немножко раскрою термины, чтобы была понятна абсурдность вопроса:
Вопрос с передачей потока управления между потоками управления.
R> Вообщем вопрос: как можно из дополнительного потока передать сигнал в основной поток, так чтобы следующие действия выполнялись именно в основном потоке?
Все зависит от того, что в этот момент делает основной поток. Так что он делает?
... << RSDN@Home 1.2.0 alpha 5 rev. 31 on Windows 7 6.1.7601.65536>>
Здравствуйте, Roman039, Вы писали:
R>Я, читая эту статью, нашел и описание вами предложенного варианта. С Control'ом нельзя: UI не обязательно будет WinForms.
Цикл выборки сообщений будет? Или тоже не обязательно?
... << RSDN@Home 1.2.0 alpha 5 rev. 31 on Windows 7 6.1.7601.65536>>
Здравствуйте, Roman039, Вы писали:
R>Подскажите, а такой манёвр (переключение контекста синхронизации) возможен только при использовании UI-потока?
Нет никакого такого маневра. Тебе опять чего то не то показалось. Контекст синхронизации это всего лишь способ абстрагироваться от конкретного способа синхронизации для конкретного потока.
R> Вообщем если инициализировать контекст в форме, то все работает как требуется.
Его вообще обычно никто явно не инициализирует.
R> А если попробовать реализовать подобную схему с обычными (не UI) классами, то значение SinhronizationContext.Current сбрасывается в null и фактического переключения не происходит.
Инициализация SyncContext происходит неявно. В частности, в случае WinForms, это делается в конструкторе базового класса Control. Т.е. при создании любого элемента управления контекст инициализируется. Чтобы не заморачиваться, использовать следует класс AsyncOperationManager, а не контекст напрямую. И да, если цикла выборки сообщений в потоке нет, то отработка, естественно, будет происходить в потоке вызвавшем (или в отдельном, если испольтзовать Post).
... << RSDN@Home 1.2.0 alpha 5 rev. 31 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Он же написал — передать управление из другого потока в основной
"Нет, все понятно, но шо конкретно?..." (С) И, главное, зачем?
Здравствуйте, HowardLovekraft, Вы писали:
HL>Здравствуйте, AndrewVK, Вы писали:
AVK>>Он же написал — передать управление из другого потока в основной HL>"Нет, все понятно, но шо конкретно?..." (С) И, главное, зачем?
Вы помогли мне решить вопрос с передачей управления в UI-поток. Этот вопрос закрыт. Вопрос о передаче управления в любой другой поток возник из любопытства (просто хотел уточнить можно так сделать или нет) и не имеет отношения к решению задачи. Я нашел статью в которой приводится пример переключения контекста без явного указания на UI-поток (т.е. не указано, что однажды создавалось что то вроде Control'а). Естественно в чистом виде этот пример не заработал (потому что SynhronizationContext.Current оставался null'ом). Вот я и хотел уточнить этот момент.