Как обрабатывать сигнал один раз во многих потоках в C#

#c# #multithreading

#c# #многопоточность

Вопрос:

В C # у меня есть несколько объектов, каждый со своим собственным потоком, который может выполнять операцию на основе внешнего сигнала. Весь объект содержит список всех других объектов.
Мне нужно, чтобы поступивший сигнал выполнялся только в одном объекте в одном потоке. Этот сигнал приведет к удалению всех объектов, поэтому для одного и того же объекта не будет поступать несколько сигналов.
Наиболее вероятно, что сигнал поступит для всех потоков одновременно, но могут быть задержки. Поскольку сигнал приведет к уничтожению всех потоков и не будет доставлен уничтоженным, задержки можно игнорировать.
Я думал о том, чтобы сделать lock(this) , а затем заблокировать каждый объект в списке, но я не знаю, как выполнить блокировку, не создавая для нее область видимости (имеется в виду разблокировка вручную, а не когда область блокировки заканчивается).
Дополнительная проблема заключается в том, что если один из них уже начал блокировку, а другой пытается заблокировать его, он будет просто ждать, а не игнорировать его.
Кроме того, у меня нет контроля над Thread объектами, и даже если есть способ их извлечь, любое прикосновение к Thread объекту, скорее всего, приведет к сбою приложения. Ответы, подобные приостановке всех других потоков, не будут работать.
Итак, как мне получить сигнал, на который воздействует ровно один объект, и игнорировать его остальными?

Комментарии:

1. несколько вещей: по крайней мере, пожалуйста, предоставьте некоторый псевдокод того, как ваш объект (ы) получает сообщение и как они уничтожают другие объекты и / или самих себя. Что делают ваши объекты? Кто получит поздние сигналы, если все объекты будут уничтожены?

2. Кроме того, как объект, получающий сообщение, узнает обо всех других объектах, которые необходимо удалить?

3. Я не знаю, как это работает… Это платформа C , которая использует встроенную .NET framework. он вызывает метод в моем объекте при получении сигнала, я должен вызвать метод destroy, когда я хочу, чтобы он был уничтожен. Я сохраняю список всех объектов во всех своих объектах. Когда объект уничтожается, все его ссылки каким-то образом устанавливаются в null.

4. Можете ли вы предоставить общий интерфейс того, как это работает? Т.Е. как объект получает сообщение в вашей системе?

Ответ №1:

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

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

Комментарии:

1. Родительский поток — хорошая идея. Я просто отмечу один из потоков как родительский при его создании и передам его всем другим потокам, которые присоединяются к нему.

Ответ №2:

Я думаю, что понимаю, что происходит сейчас, но поправьте меня, если это неправильно:

  • предположительно, все ваши объекты одного типа
  • список объектов является статическим (если нет, то подумайте о том, чтобы сделать это более эффективным)
  • любой объект может получить сигнал, но только один будет его обрабатывать.

Итак, если мы предположим, что у каждого вашего объекта есть метод для получения сигнала, то я бы подумал, что это может быть достойный дизайн:

 public class Thing
{
    private static volatile bool _tooLate = false;
    private static List<Thing> _theWorld; // contains everything
    private static readonly object _sync = new object();

    public Thing(){/*...*/}

    // a static method to process the signal
    public static void ProcessSignal(Signal signal)
    {
        // make sure you're not late to the party
        if(signal == signal.DestroyTheWorld amp;amp; !_tooLate)
        {
            // You won't destroy the world yet... but you'll give it a shot!
            BringAboutTheDestruction();
        }
    }

    // a static method to bring about the destruction of the world! MUAHAHAHAHA
    public static void BringAboutTheDestruction()
    {
        // you might not be late for the party, 
        // but you still have to get past the doorman
        lock(_sync)
        {
            // you finally got in, but you might get kicked out!
            if(!_tooLate)
            {
                // Apocalypse now!!! There is no turning back
                foreach(Thing t in _theWorld)
                {
                    t.Destroy();
                }
                _theWorld.Clear();
            }
            _tooLate = true;
        }
    }
}
  

Ответ №3:

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

 void CreateThreads ()
{
   global_event = new EventWaitHandle (false, EventResetMode.AutoReset);

   for each thread to create
   {
     create thread a pass the global_event object
   }
}

void ThreadProc (EventWaitHandle global_event)
{
  create local_event; // this is set when the thread needs to do something

  bool quit = false;

  while (!quit)
  {
    switch (EventWaitHandle.WaitAny (new WaitHandle [] { global_event, local_event })
    {
    case 0: // global 
      shut down other threads
      quit = true;
      break;

    case 1: // local
      do local signal processing
      break;
    }
  }
}
  

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