Wednesday, May 7, 2014

How To Limit The Thread Count Use By The Task Library?

Today I want to cover the topic about how to limit the thread use by the Task library which is available in .NET 4.5. This post is related to my previous async and await syntax post.

First of all, do you know that when one Task start to run, a new thread will be spawn for that task? Task is supposed to be used for asynchronous operation, however, with its thread spawning behavior, it also can be used for multi-threading operation. More detail info, check out this Task Parallelism article.

There are 3 ways to create a Task:

Option 1:

Using Task.Factory.

Task t1 = Task.Factory.StartNew(() =>
{
    Console.WriteLine(DateTime.Now.ToString() + " - Task fired.");
    Thread.Sleep(5000);

});


Option 2:

Using Task.Start() method.

Task t2 = new Task(() =>
{
    Console.WriteLine(DateTime.Now.ToString() + " - Task fired.");
    Thread.Sleep(5000);
});


t2.Start();


Option 3:

Using Task.Run() method.

Task t3 = Task.Run(() =>
{
    Console.WriteLine(DateTime.Now.ToString() + " - Task fired.");
    Thread.Sleep(5000);

});


To me, the option 1 and 2 are the same, though the MSDN article suggested to use Factory when you do not want to separate the task creation and scheduling, but using Task explicitly also can do the job. May be Factory pattern look elegant? :P

Option 3 is different. The Task.Run() method only uses the available thread from the thread pool. Unlike option 1 and 2, both method spawn a new thread whenever a Task is started. Sometime, we need to limit the thread spawning because thread is resource expensive, it will consume all the memory and processing power that you have. The reason is the processor can only process one thing at a time, and due to the speed of the processor nowadays is extremely fast, it looks like all the tasks are processing concurrently. In fact, the processor is actually keep switching between threads to process bit by bit until the end and that created the illusion to many people think that their tasks are running concurrently. Even with the Hyper-Threading processor technology today, my Intel i7 4 cores 8 threads processor can only process 8 threads concurrently.



Therefore, we cannot set the maximum thread limit less than the logical processor count. For my processor, it has 8 logical processor, I am not allowed to set the maximum thread value less than 8. Also, the default minimum thread count is following logical processor count which is 8.

You can use the following method to check your current thread pool thread limit:

ThreadPool.GetMinThreads(out workerThreadCount, out ioThreadCount);
ThreadPool.GetMaxThreads(out workerThreadCount, out ioThreadCount);

Then, change the thread pool limit with the following code:

//First parameter is the number of thread in the pool
//Second parameter is the number of async I/O thread in the pool
//Both value cannot be less than your logical processor count
ThreadPool.SetMaxThreads(10, 10);

Then, combine with using Task.Run() method, you will notice only 10 threads will be spawn and working concurrently.



See the highlighted red box, there are 8 threads are actually running concurrently even I have set the max worker thread limit as 10. The other 2 thread spawn slightly later.

The following is the final piece of code:

class Program
{
    static void Main(string[] args)
    {
        int workerThreadCount;
        int ioThreadCount;

        ThreadPool.GetMinThreads(out workerThreadCount, out ioThreadCount);

        Console.WriteLine("Default min worker thread: " + workerThreadCount.ToString());
        Console.WriteLine("Default min I/O thread: " + ioThreadCount.ToString());

        ThreadPool.GetMaxThreads(out workerThreadCount, out ioThreadCount);

        Console.WriteLine("Default max worker thread: " + workerThreadCount.ToString());
        Console.WriteLine("Default max I/O thread: " + ioThreadCount.ToString());

        Console.ReadKey();

        //First parameter is the number of thread in the pool
        //Second paramter is the number of async I/O thread in the pool
        //Both value cannot be less than your logical processor count
        ThreadPool.SetMaxThreads(10, 10);

        ThreadPool.GetMinThreads(out workerThreadCount, out ioThreadCount);

        Console.WriteLine("Current min worker thread: " + workerThreadCount.ToString());
        Console.WriteLine("Current min I/O thread: " + ioThreadCount.ToString());

        ThreadPool.GetMaxThreads(out workerThreadCount, out ioThreadCount);

        Console.WriteLine("Current max worker thread: " + workerThreadCount.ToString());
        Console.WriteLine("Current max I/O thread: " + ioThreadCount.ToString());

        for (int i = 1; i <= 100; i++)
        {
            //Task Factory
            //Task t1 = Task.Factory.StartNew(() =>
            //{
            //    Console.WriteLine(DateTime.Now.ToString() + " - Task fired.");
            //    Thread.Sleep(10000);
            //});

            //Normal Task Start
            //Task t2 = new Task(() =>
            //{
            //    Console.WriteLine(DateTime.Now.ToString() + " - Task fired.");
            //    Thread.Sleep(10000);
            //});

            //t2.Start();

            //Thread Pool Task Run
            Task t3 = Task.Run(() =>
            {
                Console.WriteLine(DateTime.Now.ToString() + " - Task fired.");
                Thread.Sleep(10000);
            });
        }

        Console.ReadKey();
    }
}

Running the program long enough, you will see the 10 threads limit behavior since I purposely make every thread to sleep for 10 seconds. You can uncomment the code that use Factory and Task.Start(), you will notice they spawn threads uncontrollably.








Sunday, April 20, 2014

Windows Service Monitoring Console

Problem

Today topic is covering how to make windows service interact with any desktop application. I am sure every windows service developer would face a challenge with the "no user interface" windows service nature behavior whereby you cannot know whether your service is actually doing its job or actually hanging.

The windows services are running in session 0, an environment where the application is running with no user context, no user interface, they are separated from desktop applications group. Last time in Windows XP/2003 or earlier OS, windows services allow to interact with any desktop application, and because of having this flexibility, windows service possess security threat and always become a common attack target. Therefore, in Windows Vista/2008 or newer OS, the windows services are protected and isolated from the desktop applications. From then onwards, windows service cannot direct interact with any desktop application. More info about session 0 isolation.

Solution

Even with session 0 isolation, the windows service is not actually entirely locked down, we can still make it to interact it with other application by using RPC (Remote Procedure Call) or .NET Remoting, but they are old, today I am sharing how to use WCF instead.

I have created a WPF application which is used to monitor the windows service activity. You will see all the logs written by the windows service appearing in the monitor console in real time.

Concept

The WPF application is hosting a WCF host with named pipe communication channel since the monitoring console and the windows service are sitting in the same machine. Whenever the windows service is writing log, it will call the WCF service. The WCF service receive the log and write it to a text box in WPF application.

Challenge

The windows service is running all the time even without user logon, but the monitoring console can only be run with a logon user. Therefore, the windows service cannot always call the WCF service, it would encounter error when there is no active user session and monitoring console is not running. Therefore, it should call only when it detect the monitor console is running.

Implementation

Monitoring Console

WCF Service

Create a service that receive log from windows service.

The service contract definition:

[ServiceContract]
public interface ILoggingService
{
    [OperationContract]
    void WriteLog(string value);
}

The service implementation:

public class LoggingService : ILoggingService
{
    public void WriteLog(string value)
    {
        //Locate the text box and write the receive value into it
        MainWindow main = (MainWindow)Application.Current.MainWindow;
        main.LogText.Text += value + Environment.NewLine;
    }
}

WPF Application

Create a simple interface that contain a text box. This text box will display all the log sent by the windows service.


<Window x:Class="WinSvcMonitoringTool.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox x:Name="LogText" Margin="10" TextWrapping="Wrap"/>
    </Grid>
</Window>

Create WCF host when the application is opened, activated and all controls are rendered.

public partial class MainWindow : Window
{
    private ServiceHost _host = null;

    public MainWindow()
    {
        InitializeComponent();
    }
       
    protected override void OnContentRendered(EventArgs e)
    {
        //Start WCF service automatically when the window is activated
        try
        {
            _host = new ServiceHost(typeof(LoggingService));

            //Create Metadata exchange for the service
            ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior();
            _host.Description.Behaviors.Add(mexBehavior);

            //Add service endpoints for the service and mex
            _host.AddServiceEndpoint(typeof(ILoggingService), new NetNamedPipeBinding(), "net.pipe://localhost/WinSvcMonitoringService/LoggingService.svc");
            _host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexNamedPipeBinding(), "net.pipe://localhost/WinSvcMonitoringService/LoggingService.svc/mex");
            _host.Open();

            LogText.Text += "WCF Host started succeessfully." + Environment.NewLine;
        }
        catch (Exception ex)
        {
            if (_host != null)
                _host.Abort();

            MessageBox.Show("Service Host Error : " + ex.Message);
        }

        base.OnContentRendered(e);
    }

    protected override void OnClosed(EventArgs e)
    {
        //Close the WCF service host when the form is closed
        _host.Close();
        base.OnClosed(e);
    }

}

That's all for the monitoring console. It is ready for the windows service to call and consume. Now start coding the windows service.

Windows Service

The windows service is doing a simple write log activity for every second. Therefore, during the service OnStart event:

Task task = new Task(() =>
{
    while (true)
    {
        WriteLog("Hello World!");

        Thread.Sleep(1000);
    }
}, _source.Token);


task.Start();

Now, use "Add Service Reference" to generate the WCF client proxy class. Due to the service is created with NetNamedPipeBinding, enter the service address start with net.pipe.


Then, use the proxy class as follow:

try
{
    using (LoggingServiceClient proxy = new LoggingServiceClient())
    {
        proxy.WriteLog(log);
        proxy.Close();
    }
}
catch(Exception ex)
{
    //Error occur, write the detail to the event log
    this.EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);

}

At this point of time, the windows service should be able to call the WCF service hosted in the WPF monitoring console. You will notice Hello World! is written to the text box every second.


Detect Monitoring Console

However, whenever the monitoring console is closed, the WCF host is closed as well, and the windows service will encounter error because the WCF service no longer exists. In order to solve this challenge, I have to implement something that the windows service know if the monitoring console is running or not. If it is running, it will call the WCF service, otherwise, do nothing or write the log to text file.

Windows Management Instrumentation (WMI)

WMI can be used to manage almost anything related to OS. For this case, I am going to use the ManagementEventWatcher class library to watch out for my monitoring console is opened or closed. In order to do that, I need to write a WMI query to monitor the process name that I want:

//The query to monitor WinSvcMonitoringTool.exe process is started or stopped
private const string _pStartQuery = "SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName = 'WinSvcMonitoringTool.exe'";
private const string _pStopQuery = "SELECT * FROM Win32_ProcessStopTrace WHERE ProcessName = 'WinSvcMonitoringTool.exe'";

Note: WinSvcMonitoringTool.exe is my WPF application name.

Pass in the query to the ManagementEventWatcher constructor.

_pStartWatcher = new ManagementEventWatcher(new WqlEventQuery(_pStartQuery));
_pStartWatcher.EventArrived += new EventArrivedEventHandler(ProcessStartEvent);
_pStartWatcher.Start();

_pStopWatcher = new ManagementEventWatcher(new WqlEventQuery(_pStopQuery));
_pStopWatcher.EventArrived += new EventArrivedEventHandler(ProcessStopEvent);
_pStopWatcher.Start();

Then, subscribe EventArrived event handler. So that, when it detected the monitoring console process is started, it will flag the window service to start call WCF. And also if the process is closed, the window service will stop the WCF call.

private void ProcessStartEvent(object sender, EventArrivedEventArgs e)
{
    //flag to call WCF service
    _monitorConsoleIsDetected = true;
}

private void ProcessStopEvent(object sender, EventArrivedEventArgs e)
{
    //flag to stop calling WCF service
    _monitorConsoleIsDetected = false;

}

So, my write log method has become like this after making use of the flag.

private void WriteLog(string log)
{
    //Only write log when there is any user session is active and the monitoring tool is opening
    if (_monitorConsoleIsDetected)
    {
        try
        {
            using (LoggingServiceClient proxy = new LoggingServiceClient())
            {
                proxy.WriteLog(log);
                proxy.Close();
            }
        }
        catch(Exception ex)
        {
            //Error occur, write the detail to the event log
            this.EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
        }
    }

}

Summary

After done with the WMI event watcher. Start the windows service first, it will do nothing. When you run the monitoring console exe, the windows service will automatically detect its existence, then start calling the WCF service hosted in the monitoring console. Then, the log will start appearing in the monitoring console. If you shutdown the application or logoff from the OS, the windows service will know it and then stop calling the WCF service. The only drawback about this console monitoring is you have to run it with Administrator mode, otherwise the WCF service host will not run properly.

If you are interested with my source code, feel free to download it from HERE.







Saturday, April 12, 2014

Workflow Delay Activity Does Not Continue

One of my colleague came to me and showed me that her idle workflow instance which is currently waiting for delay activity to time up to continue the work, is actually not working. The workflow instance is not activated and continue the process automatically. I am here to share my troubleshooting experience.

It is a workflow service hosted in IIS 7.5, and involving AppFabric 1.1. Here is the story, the following is a sample how the workflow design look like:


The workflow allows user to supply a future datetime value to the service, and then the workflow instance will sleep until the time has come and then wake up and continue the process. So, as you see above, the Receive activity will accept the StartDateTime parameter, and then the Assign activity there will calculate the time span for how long the instance need to delay. After the Delay activity, will do a WriteLine activity.

The problem is the WriteLine activity never fire, the instance is still stuck at Delay activity. If you happen to encounter the similar problem now, you may want to check whether there is a missing net.pipe protocol enabled at IIS application level. The reason is workflow instance normally will turn to Idle state after some time, then the AppFabric Workflow Management Service (WMS) rely on net.pipe to activate workflow instances when the time comes.

Check at Web Site level:


Check at Web Application level:



Next, you may want to check your AppFabric persistence database. From the web server, is it connectable to the persistence database. Make sure you are able to ping the database IP address or hostname, and also confirm the firewall clearance. Note: SQL Server default TCP port is 1433.

When everything are confirmed correct, check the server time in web server and database server. My actual root cause is the server time are not the same in between web and database servers. The database server had 3 minutes time later than web server.

If you execute the following query from the AppFabric persistence database:

SELECT * FROM [System.Activities.DurableInstancing].[InstancesTable]

You will notice that your workflow instance has the PendingTimer column value is a past date compare to the web server date. Note: PendingTimer is in UTC time, you need to offset the hours manually base on your location. It is the time when your workflow instance should be re-activated by WMS. With the date time value discrepancy between the web and database server, I guess WMS ignore the instance activation since it is a past date.

Web Server time: 2014-04-10 21:00:00
Database Server time: 2014-04-10 20:57:00

The problem was solved after making both servers time in sync.

If you wonder what can still be done to those existing idle instances, what can you do to "wake" it up? The workaround is to suspend those idle instances, then resume them by using the AppFabric Dashboard in IIS.


Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...