0

I have a WCF sample with ConcurrencyMode = Multiple and the binding mode is WSHttpBinding

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
    public class StudentService : IStudentService
    {
        public string GetStudentInfo(int studentId)
        {
            Console.WriteLine(string.Format("{0} - [GetStudentInfo] Receive request, thread id {1}", DateTime.Now, System.Environment.CurrentManagedThreadId));

            Thread.Sleep(5000); // For testing, this sleeping is used to make this function runs longer

            string studentName = string.Empty;
            switch (studentId)
            {
                case 1:
                    {
                        studentName = "Johnson";
                        break;
                    }
                case 2:
                    {
                        studentName = "Henry";
                        break;
                    }
                default:
                    {
                        studentName = "No student found";
                        break;
                    }
            }

            Console.WriteLine(string.Format("{0} - [GetStudentInfo] Leave request, thread id {1}", DateTime.Now, System.Environment.CurrentManagedThreadId));
            
            return studentName;
        }
}

I would like to send multiple requests to this service by using threading, but based on the log, it received requests sequentially

This is my client:

public static T CreateChannelFactory<T>()
        {
            try
            {
                // Create channel to connect to service

                ChannelFactory<T> myChannelFactory = new ChannelFactory<T>(new WSHttpBinding());
                myChannelFactory.Endpoint.Address = new EndpointAddress("http://localhost:9091/StudentService");

                T channel = myChannelFactory.CreateChannel();


                return channel;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        static void Main(string[] args)
        {
            StudentService.IStudentService channel = CreateChannelFactory<StudentService.IStudentService>();

           
            Thread[] arr = new Thread[10];
            for (int i = 0; i < arr.Length; i++)
            {
                arr[i] = new Thread(new ThreadStart(() => channel.GetStudentInfo(1)));
                
            }

            for (int i = 0; i < arr.Length; i++)
            {
                arr[i].Start();
            }

            for (int i = 0; i < arr.Length; i++)
            {
                
                arr[i].Join();
            }

            Console.ReadKey();
        }

Based on the log, the service received requests sequentially

-> Receive then Leave, then Receive then Leave,..., and then Receive then Leave enter image description here

But, when I tried sending the first request before sending other requests

static void Main(string[] args)
        {
            StudentService.IStudentService channel = CreateChannelFactory<StudentService.IStudentService>();
            channel.GetStudentInfo(1); // THE FIRST REQUEST HERE

           
            Thread[] arr = new Thread[10];
            for (int i = 0; i < arr.Length; i++)
            {
                arr[i] = new Thread(new ThreadStart(() => channel.GetStudentInfo(1)));
                
            }

            for (int i = 0; i < arr.Length; i++)
            {
                arr[i].Start();
            }

            for (int i = 0; i < arr.Length; i++)
            {
                
                arr[i].Join();
            }

            Console.ReadKey();
        }

And the service can receive multiple requests

enter image description here

-> Many Receive at the same time

The service ConcurrencyMode = Multiple, but why do I need to send the first request? Any reason for that? Thank you

******** Host project:

static void Main(string[] args)
    {
        ServiceHost studentServiceHost = null;
        try
        {
            //Base Address for StudentService
            Uri httpBaseAddress = new Uri("http://localhost:9091/StudentService");

            //Instantiate ServiceHost
            studentServiceHost = new ServiceHost(typeof(StudentService.StudentService),
                httpBaseAddress);

            //Add Endpoint to Host
            studentServiceHost.AddServiceEndpoint(typeof(StudentService.IStudentService),
                                                    new WSHttpBinding(), "");

            //Metadata Exchange
            ServiceMetadataBehavior serviceBehavior = new ServiceMetadataBehavior();
            serviceBehavior.HttpGetEnabled = true;
            studentServiceHost.Description.Behaviors.Add(serviceBehavior);

            //Open
            studentServiceHost.Open();
            Console.WriteLine("Service is live now at : {0}", httpBaseAddress);
            Console.ReadKey();
            studentServiceHost.Close();

        }

        catch (Exception ex)
        {
            studentServiceHost = null;
            Console.WriteLine("There is an issue with StudentService:" + ex.Message);
        }
    }

******** Channel Factory create:

public static T CreateChannelFactory<T>()
    {
        try
        {
            // Create a channel to connect to the service

            ChannelFactory<T> myChannelFactory = new ChannelFactory<T>(new WSHttpBinding());
            myChannelFactory.Endpoint.Address = new EndpointAddress("http://localhost:9091/StudentService");
            
            T channel = myChannelFactory.CreateChannel();

            return channel;
        }
        catch (Exception ex)
        {
            throw ex;
        }

    }
Hau Vo
  • 1
  • 1
  • The ServiceChannel does lock on several internal object instances when opening and calling a service. I assume in your first example all threads are locked in the EnsureOpen call except one but I can't explain why none of them progress any sooner. In your second example I assume you'll see the same result if you call `channel.Open();` first instead of `channel.GetStudentInfo(1);` after you created the channel. In general I would not re-use /share a channel between multiple threads, specially once a channel becomes faulted. Create a channel instance in each thread instead. – rene May 19 '23 at 08:15
  • In fact, I tested it based on two of your cases. Found some differences between my results and yours. My two results are similar. I'm also curious about your results. I think it might have something to do with the configuration of your WCF sending data. Actually my test results are based on a local WCF service and then sent locally. I think this change can continue to add a delay in the **Thread.Start()** loop, which also works well in my tests. – QI You May 19 '23 at 09:55
  • Hi rene, I added CreateChannelFactory function above. As I know, CreateChannel() will call Open internally https://stackoverflow.com/questions/32264730/does-channelfactory-createchannel-actually-open-connection By the way, thanks for your advice – Hau Vo May 23 '23 at 10:14
  • Hi @QIYou, I added the Host project above, which included the configurations. I just use a sample of WSHttpBinding. Could you please compare it with yours? Thank you. I see your idea when using the delay between stating threads, but it will be like the case in that I sent the first request, then sent other requests. – Hau Vo May 23 '23 at 10:25

1 Answers1

0

You can try to increase the delay time in the service.

public string GetStudentInfo(int studentId)
        {
            Console.WriteLine(string.Format("{0} - [GetStudentInfo] Receive request, thread id {1}", DateTime.Now, System.Environment.CurrentManagedThreadId));

            Thread.Sleep(15000); // For testing, this sleeping is used to make this function runs longer

            string studentName = string.Empty;
            switch (studentId)
            {
                case 1:
                    {
                        studentName = "Johnson";
                        break;
                    }
                case 2:
                    {
                        studentName = "Henry";
                        break;
                    }
                default:
                    {
                        studentName = "No student found";
                        break;
                    }
            }

            Console.WriteLine(string.Format("{0} - [GetStudentInfo] Leave request, thread id {1}", DateTime.Now, System.Environment.CurrentManagedThreadId));

            return studentName;
        }

OutPut: enter image description here

QI You
  • 124
  • 4
  • Hi @QI You, I tried to increase to 15s as you did, and my result was the same as yours. Based on this, I feel like The Multiple setting only affects after it receives the first request + a short time. I don't get why it does not affect immediately. Do you have any idea? – Hau Vo Jun 07 '23 at 06:42
  • The explanation of multiple in MSDN goes like this: Multiple: In this scenario, multiple requests can be handled by the WCF service object at any given moment of time. In other words, requests are processed at the same time by spawning multiple threads on the WCF server object. So you have great throughput here but you need to ensure concurrency issues related to WCF server objects. So I think the execution time of your operation in the process is too short and then causes this problem. So I increased the waiting time. – QI You Jun 15 '23 at 07:10