Как я могу уведомить основной поток о каком-либо сообщении в другом потоке без блокировки и ожидания? - PullRequest
1 голос
/ 18 января 2010

Я пишу компонент c #, который будет использоваться только внутри моей компании. Компонент инкапсулирует связь с рядом серверов, с которыми должны взаимодействовать определенные настольные приложения. Серверы могут отправлять незапрошенные сообщения компоненту, которые «перехватываются» в отдельном потоке.

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

Есть ли простой способ сообщить о запущенном потоке? Основной поток будет выполнять все виды постоянной обработки, а поток сообщений будет постоянно вращаться в ожидании сообщений. Возможно, стоит отметить, что поток сообщений инкапсулирован в сторонней библиотеке. Получив сообщение, он выполняет обратный вызов. Я бы хотел, чтобы обратный вызов выполнял что-то вроде mainthread.notify (message).

Edit: Извините, я не слишком ясно поняла, что я хочу сделать основной темой. Я не хочу, чтобы основной поток немедленно обрабатывал сообщение, отправленное потоком сообщений. Я хочу, чтобы он обрабатывал сообщение в ближайшее время (например, как работает цикл сообщений WinForms).

Редактировать 2:

Сценарий:

Console App created on Thread1  
Thread2 created, which spins listening for Messages
Console App runs as normal
Message arrives on Thread2
Thread2 fires event MessageReady(sender, message)
Thread2 continues spinning
At the earliest convenience, Thread1 processes the message from MessageReady

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

Ответы [ 4 ]

1 голос
/ 18 января 2010

Нет, это невозможно. Поток должен бездействовать, прежде чем вы сможете вставить в него код. Это делается, например, Control.BeginInvoke (Windows Forms) или Dispatcher.BeginInvoke (WPF). Эти библиотеки пользовательского интерфейса часто требуют выполнения кода в потоке пользовательского интерфейса, поэтому они явно поддерживают маршалинг вызовов в поток пользовательского интерфейса.

Важно, чтобы поток находился в состоянии ожидания. У вас возникли бы ужасные проблемы с повторным входом, если бы .NET поддерживал какой-то метод асинхронного внедрения.

1 голос
/ 18 января 2010

Если ваш компонент будет в форме окна, то вот один из способов достижения вашей цели:

В вашем коде компонента:

public event EventHandler MessageReceived;

private void UnsolicitedMessageReceived(...)
{
  if (MessageReceived != null)
  {
   // this will invoke on UI thread
   Parent.Invoke(MessageReceived, this, EventArgs.Empty);
  }
}

В вашей форме вы можете иметь:

MyCoolComponent1.MessageReceived += new EventHandler(MessageReceived);

private void MessageReceived(object sender, EventArgs e)
{
  // do some stuff here
}
0 голосов
/ 18 января 2010

Один из вариантов - передать делегата из вашего основного потока дочерним потокам, которые они могут использовать в качестве обратного вызова, чтобы сообщить, что у них есть сообщение. Дочерние потоки могут передавать сообщение через обратный вызов, который сохраняет сообщение в коллекции на основе памяти или в постоянном хранилище, и основной поток проверяет это при необходимости.

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

0 голосов
/ 18 января 2010

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

Используйте семафор, чтобы ваш рабочий поток ожидал новых данных, если очередьпуст, и пульсировать этот семафор, если вы добавляете новое сообщение в пустую очередь.Это не позволяет им тратить циклы ЦП на постоянный опрос очереди.

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

РЕДАКТИРОВАТЬ: При повторном чтении вопроса, я не уверен, если ваша проблема в том, что вам действительно нужен поток, чтобы сделать некоторую работу немедленно.Если это так, я не вижу возможности этого, поскольку вы не можете просто вводить код в случайное время - вы, скорее всего, сломаете все, что выполнялось в этот момент.

...