简体   繁体   中英

WCF Service With A Worker Thread?

I am trying to write a WCF Servce which will reside in a Windows Service. This WCF Service will just add strings to a list and then the worker thread will process this list periodically. What is the best way to achieve this? I have read conflicting examples which has left me confused. What is the best way of the service and thread sharing a list object?

Update: Thanks for the replies so far. Just to clarify, I am not struggling with the synchronization of the list or how to make it thread safe. That all seems the same principles as what I am used to in C++. What I am struggling with is where to define this list so it is accessible from both the WCF service and the worker thread. In C++ I would have created the list at global scope but C# doesn't have a global scope. If I define it in the WCF service class, the thread can't see it. If I definite it in the service class (where the thread function is defined and started) the WCF service cannot see it. I am sure I did something similar to this in ATL some time ago but it's Friday afternoon and the grey cells have given up for the day.

Update2: Where should I be defining the worker thread? In the Windows Service class (ie the host) or in the WCF Service? Should the WCF Service be a singleton service which has a list member and a thread function? That solves the access issue. Having done a lot of COM, I'm thinking of a WCF service as a COM component with the instance dying after it is accessed. This led me to want to put the static list and thread function in the Windows service class. That still feels the more natural place for it but perhaps I am just not thinking in a .NET way.

1) Create Windows Service

2) Host WCF in the WS ( http://msdn.microsoft.com/en-us/library/ms731758.aspx )

3) Make a thread synchronization ( http://www.albahari.com/threading/part2.aspx )

4) profits!

Here's a quick little shell which uses the concurrent collections in .NET 4 (for that matter, I'm also using for this example the Task Parallel Library in .NET 4). Your problem statement seems to be a classic producer/consumer on differing threads or processes, so this should give you a decent idea of how to structure things:

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();
        }
    }
}

The general pattern is to start the service host in the on start of service. Once Started you can create your worker thread. The worker thread and the service calls will need to access a shared data structure which will need to be synchronized. Have the service deposit items on the shared data object and signal a Wait handle to run the worker thread. Once the worker completes reset the state of the wait handle.

I think what you are asking for is specifically, how do you pass a reference of the collection instance into the WCF service instance. Here is the crux of the problem: normally the ServiceHost code will spin up WCF service instances based on the InstanceContextMode setting for the service (the default value is PerSession). This means that it will spin up an instance per client session on demand. Since your host code can't directly access these automatically created instances then you can't inject the shared collection.

One solution is for your host to provide a WCF service instance with the shared collection added through either constructor parameter or a property setter. There is a constructor for ServiceHost that takes this instance but it has major tradeoff. This approach means you are creating a singleton WCF service ( InstanceContextMode = Single ) so the scalability will suffer. You can mitigate this somewhat by setting ConcurrencyMode to multiple but you'll also have to write your WCF service code to handle internal synchronization of resources.

You could start the worker thread in the static constructor of the WCF service. There would, of course, be only one worker thread regardless of the ConcurrencyMode and InstanceContextMode . If that is what you want then the following code might work for you.

[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);
  }
}

I have used the producer-consumer pattern to implement the worker thread logic. The BlockingCollection class provides an easy mechanism for implementing that pattern.

. In C++ I would have created the list at global scope but C# doesn't have a global scope.

Any public static member is globally visible. Of course you'd have to implement your own synchronisation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM