Создать поток и рабочую очередь, только если ресурс занят - PullRequest
0 голосов
/ 13 февраля 2012

Надеюсь, что кто-то может помочь мне правильно спроектировать это.

В моем коде TCP у меня есть функция SendMessage (), которая пытается записать на провод. Я пытаюсь спроектировать вызов так, чтобы он перешел к модели производителя / потребителя, если происходит много одновременных запросов, но в то же время остается однопоточным, если нет одновременных запросов (для максимальной производительности).

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

То, что у меня есть, что-то вроде (псевдокодирование):

SendMessage(msg) {

    if(Monitor.TryEnter(wirelock,200)) {

       try{
          sendBytes(msg);
       }
       finally {
          Monitor.Exit...
       }
   }
   else {

     _SomeThreadSafeQueue.add(msg)

     Monitor.TryEnter(consumerlock,..

          Task.Factory.New(ConsumerThreadMethod....
    }
}


ConsumerThreadMethod() {

   lock (wirelock) {

         while(therearemessagesinthequeue)

               sendBytes...

   }

}

Какие-нибудь очевидные условия гонки?

РЕДАКТИРОВАТЬ: нашел недостаток в последнем. Как насчет этого вместо этого?

SendMessage(msg) {

    if(Monitor.TryEnter(wirelock)) {

       try{
          sendBytes(msg);
       }
       finally {
          Monitor.Exit...
       }
   }
   else {

         _SomeThreadSafeQueue.add(msg)

        if (Interlocked.Increment(ref _threadcounter) == 1)
        {
            Task.Factory.StartNew(() => ConsumerThreadMethod());

        }
        else
        {
            Interlocked.Decrement(ref _threadcounter);

        }      
    }
}


ConsumerThreadMethod() {

     while(therearemessagesinthequeue)
           lock (wirelock) {
               sendBytes...
            }
      }

     Interlocked.Decrement(ref _threadcounter);
}

Таким образом, в основном используется блокированный счетчик как способ создания только одного потока (при необходимости)

1 Ответ

0 голосов
/ 13 февраля 2012

Нет явных гонок, но TryEnter - причина серьезного простоя.Я на самом деле думаю, что постоянное использование потребительского потока - лучшее решение.Если делать нечего, накладные расходы будут очень малы (потребительский поток будет спать, когда не работает, если спроектирован правильно).

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

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

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

...