#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 # не имеет глобальной области видимости.
Любой общедоступный статический элемент виден глобально. Конечно, вам пришлось бы реализовать собственную синхронизацию.