Служба WCF с рабочим потоком?

#c# #.net #multithreading #wcf

#c# #.net #многопоточность #wcf

Вопрос:

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

Обновление: Спасибо за ответы на данный момент. Просто чтобы уточнить, я не испытываю проблем с синхронизацией списка или с тем, как сделать его потокобезопасным. Похоже, что все это соответствует тем же принципам, к которым я привык в C . С чем я борюсь, так это с тем, где определить этот список, чтобы он был доступен как из службы WCF, так и из рабочего потока. На C я бы создал список в глобальной области видимости, но C # не имеет глобальной области видимости. Если я определю это в классе службы WCF, поток не сможет это увидеть. Если я определяю это в классе service (где определена и запущена функция thread), служба WCF не сможет это увидеть. Я уверен, что некоторое время назад я делал что-то подобное в ATL, но сейчас полдень пятницы, и серые клеточки сдались на этот день.

Update2: Где я должен определять рабочий поток? В классе службы Windows (т.е. на хосте) или в службе WCF? Должна ли служба WCF быть одноэлементной службой, которая имеет элемент списка и функцию потока? Это решает проблему доступа. Проделав много COM, я думаю о службе WCF как о COM-компоненте, экземпляр которого умирает после доступа к нему. Это привело меня к желанию поместить статический список и функцию потока в класс службы Windows. Это все еще кажется более естественным местом для этого, но, возможно, я просто не думаю в стиле .NET.

Ответ №1:

1) Создайте службу Windows

2) Разместить WCF в WS ( http://msdn.microsoft.com/en-us/library/ms731758.aspx )

3) Выполните синхронизацию потоков ( http://www.albahari.com/threading/part2.aspx )

4) прибыль!

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

1. Спасибо. Первая часть этого была очень полезной. Вторая часть, я уже довольно хорошо разбираюсь в синхронизации потоков со времен работы на C , поскольку на самом деле это не так уж сильно отличается. Я больше спрашивал, как я могу получить список, доступный как для объекта службы WCF, так и для рабочего потока? В C я бы просто создал список в глобальной области видимости (и сделал бы его потокобезопасным с помощью подобных Crtical разделов), но в C # нет концепции глобальной области видимости.

2. Вы должны разместить wcf внутри приложения-службы Windows. У него будет одна область применения. Хотя взгляните на некоторые шаблоны проектирования ООП, чтобы сделать все это чистым и удобным

3. Не могли бы вы расширить свой последний комментарий. Я не могу понять, как у него одна область применения. Возможно, я смотрю на неправильные примеры.

4. Смотрите: msdn.microsoft.com/en-us/library/9k985bc9(v=vs.80).aspx В методе OnStart создайте новый поток, который выполнит пример из пункта 2) моего ответа.

Ответ №2:

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

 namespace ConcurrentCollectionTest
{
    using System;
    using System.Collections.Concurrent;
    using System.Threading.Tasks;

    internal static class Program
    {
        private static void Main(string[] args)
        {
            ConcurrentQueue<string> cq = new ConcurrentQueue<string>();
            BlockingCollection<string> bc = new BlockingCollection<string>(cq);
            bool moreItemsToAdd = true;

            // Consumer thread
            Task.Factory.StartNew(() =>
            {
                while (!bc.IsCompleted)
                {
                    string s = bc.Take();

                    Console.WriteLine(s);
                }
            });

            // Producer thread
            Task.Factory.StartNew(() =>
            {
                int i = 1;

                while (moreItemsToAdd)
                {
                    bc.Add("string "   i  );
                }

                bc.CompleteAdding();
            });

            // Main Thread
            Console.ReadLine();
            moreItemsToAdd = false;
            Console.ReadLine();
        }
    }
}
  

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

1. Спасибо за попытку, но без объяснения того, что там происходит, это на самом деле мало что значит. Я не вижу, куда бы я поместил этот код в своей службе Windows, чтобы решить проблему. Хотя спасибо за ввод.

Ответ №3:

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

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

1. Ах, вот с чем я тогда борюсь. Я думал, что служба Windows была хостом службы WCF. Разве это так не работает? Я совсем новичок в .NET (особенно WCF)

Ответ №4:

Я думаю, что вы конкретно спрашиваете, как передать ссылку на экземпляр коллекции в экземпляр службы WCF. Вот суть проблемы: обычно код ServiceHost запускает экземпляры службы WCF на основе параметра InstanceContextMode для службы (значение по умолчанию — PerSession). Это означает, что он будет запускать экземпляр для каждого клиентского сеанса по требованию. Поскольку ваш хост-код не может напрямую обращаться к этим автоматически созданным экземплярам, вы не можете внедрить общую коллекцию.

Одним из решений для вашего хоста является предоставление экземпляра службы WCF с общей коллекцией, добавленной либо с помощью параметра конструктора, либо с помощью средства установки свойств. Для ServiceHost есть конструктор, который использует этот экземпляр, но у него есть существенный компромисс. Этот подход означает, что вы создаете одноэлементную службу WCF (InstanceContextMode = Single), поэтому масштабируемость пострадает. Вы можете несколько смягчить это, установив для ConcurrencyMode значение multiple, но вам также придется написать свой код службы WCF для обработки внутренней синхронизации ресурсов.

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

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

Ответ №5:

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

 [ServiceContract]
public interface IYourService
{
  [OperationContract]
  void QueueStringValue(string value);
}

[ServiceBehavior(...)]
public class YourService : IYourService
{
  private static BlockingCollection<string> s_Queue = new BlockingCollection<string>();

  static YourService()
  {
    var thread = new Thread(
      () =>
      {
        while (true)
        {
          string value = s_Queue.Take();
          // Process the string here.
        }
      });
    thread.IsBackground = true;
    thread.Start();
  }

  public void QueueStringValue(string value)
  {
    s_Queue.Add(value);
  }
}
  

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

Ответ №6:

. В C я бы создал список в глобальной области видимости, но C # не имеет глобальной области видимости.

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