2013年10月25日 星期五

Thread Pooling in C#

Thread Pooling
Thread pooling is the process of creating a collection of threads during the initialization of a multithreaded application, and then reusing those threads for new tasks as and when required, instead of creating new threads. Then every process has some fixed number of threads depending on the amount of memory available, those threads are the need of the application but we have freedom to increase the number of threads. Every thread in the pool has a specific given task. The thread returns to the pool and waits for the next assignment when the given task is completed.
Usually, the thread pool is required when we have number of threads are created to perform a number of tasks, in this organized in a queue. Typically, we have more tasks than threads. As soon as a thread completes its task, it will request the next task from the queue until all tasks have been completed. The thread can then terminate, or sleep until there are new tasks available.
threadqueue.gif
Creating thread pooling
The .Net framework library included the "System.Threading.ThreadPool" class. it was so easy to use.You need not create the pool of threads, nor do you have to specify how many consuming threads you require in the pool. The ThreadPool class handles the creation of new threads and the distribution of the wares to consume amongst those threads.
There are a number of ways to create the thread pool:
  • Via the Task Parallel Library (from Framework 4.0).
  • By calling ThreadPool.QueueUserWorkItem.
  • Via asynchronous delegates.
  • Via BackgroundWorker.
Entering the Thread Pool via TPL
The task parallel library provide the task class for enter the thread pool easy. The task class is the part of .Net Framework 4.0 .if you're familiar with the older constructs, consider the nongeneric Task class a replacement for ThreadPool.QueueUserWorkItem, and the generic Task<TResult> a replacement for asynchronous delegates. The newer constructs are faster, more convenient, and more flexible than the old.
To use the nongeneric Task class, call Task.Factory.StartNew, passing in a delegate of the target method:
using System.Threading.Tasks;using System.Threading;using System.Diagnostics;using System;
class Akshay
    static void Run()
    {
        Console.WriteLine("Welcome to the C# corner thread pool!");
    }
    static void Main() // The Task class is in System.Threading.Tasks    {
        Task.Factory.StartNew(Run);
        Console.Read();
    }
}
Output :
enthviatpl.gif
Task.Factory.StartNew returns a Task object, which you can then use to monitor the task-for instance, you can wait for it to complete by calling its Wait method.
The generic Task<TResult> class is a subclass of the nongeneric Task. It lets you get a return value back from the task after it finishes executing. In the following example, we download a web page using Task<TResult>:
class Akshay{
    static void Main()
    {
        // Start the task executing:        Task<string> task = Task.Factory.StartNew<string>
        (() => DownloadString("http://www.c-sharpcorner.com/"));
        // We can do other work here and it will execute in parallel:       //RunSomeOtherMethod();        // When we need the task's return value, we query its Result property:        // If it's still executing, the current thread will now block (wait)        // until the task finishes:        string result = task.Result;
    }
    static string DownloadString(string uri)
    {
        using (var wc = new System.Net.WebClient())
            return wc.DownloadString(uri);
        Console.Read();
    }
}
Entering the Thread Pool Without TPL using ThreadPool.QueueUserWorkItem
You can't use the Task Parallel Library if you're targeting an earlier version of the .NET Framework (prior to 4.0). Instead, you must use one of the older constructs for entering the thread pool:ThreadPool.QueueUserWorkItem and asynchronous delegates.
The ThreadPool.QueueUserWorkItem method allows us to launch the execution of a function on the system thread pool. Its declaration is as follows:
ThreadPool.QueueUserWorkItem(new WaitCallback(Consume), ware);
The first parameter specifies the function that we want to execute on the pool. Its signature must match the delegate WaitCallback.
publicdelegate void WaitCallback (object state);
Again, the simplicity of C# and the dotNet framework shine through. In just a few lines of code, I've recreated a multithreaded consumer-producer application.
using System;using System.Threading;using System.Diagnostics;public class Akshay{
      
public int id;
      
public Akshay(int _id)
      {
          id = _id;
      }
}
class Class1{
      
public int QueueLength;
      
public Class1()
      {
          QueueLength = 0;
      }
      
public void Produce(Akshay ware)
      {
         
 ThreadPool.QueueUserWorkItem(
          
new WaitCallback(Consume), ware);
          QueueLength++;
      }
      
public void Consume(Object obj)
      {
          
Console.WriteLine("Thread {0} consumes {1}",
          
Thread.CurrentThread.GetHashCode(), //{0}
          
((Akshay)obj).id); 
//{1}
          
Thread.Sleep(100);
          QueueLength--;
      }
      
public static void Main(String[] args)
      {
          
Class1 obj = new Class1();
          
for (int i = 0; i < 100; i++)
          {
               obj.Produce(new Akshay(i));
          }
         
Console.WriteLine("Thread {0}",
         
Thread.CurrentThread.GetHashCode() ); //{0}
         
while (obj.QueueLength != 0)
         {
               
Thread.Sleep(1000);
          }
          
Console.Read();
     }
}
Ouput :
stho.gif
Synchronization Objects
The previous code contains some rather inefficient coding when the main thread cleans up. I repeatedly test the queue length every second until the queue length reaches zero. This may mean that the process will continue executing for up to a full second after the queues are finally drained. I can't have that.
The following example uses a ManualResetEvent Event object that will signal the main thread to exit.
using System;using System.Threading;using System.Diagnostics;public class Akshay{
      
private bool WaitForComplete;
      
private ManualResetEvent Event;
      
public int QueueLength;
      
public int id;
      
public Akshay(int _id)
      {
           id = _id;
      }
      
public void Wait()
      {
          
if (QueueLength == 0)
          {
              
 return;
          }    
          Event = new ManualResetEvent(false);
          WaitForComplete = true;
          Event.WaitOne();
     }
     
public void Consume(Object obj)
     {
          Console.WriteLine("Thread {0} consumes {1}",
          
Thread.CurrentThread.GetHashCode(), //{0}
          
((Akshay)obj).id); //{1}          Thread.Sleep(100);
          QueueLength--;
          if (WaitForComplete)
          {
               if (QueueLength == 0)
               {
                    Event.Set();
               }
          };
     }
}
Ouput :
quserworkitem.gif
When the consuming thread finishes consuming a ware and detects that the WaitForComplete is true, it will trigger the Event when the queue length is zero. Instead of calling the while block when it wants to exit, the main thread calls the Wait instance method. This method sets the WaitForComplete flag and waits on the Event object.
Why we need thread pooling?
Thread pooling is essential in multithreaded applications for the following reasons.
  • Thread pooling improves the response time of an application as threads are already available in the thread pool waiting for their next assignment and do not need to be created from scratch.
  • Thread pooling saves the CLR from the overhead of creating an entirely new thread for every short-lived task and reclaiming its resources once it dies.
  • Thread pooling optimizes the thread time slices according to the current process running in the system.
  • Thread pooling enables us to start several tasks without having to set the properties for each thread.
  • Thread pooling enables us to pass state information as an object to the procedure arguments of the task that is being executed.
  • Thread pooling can be employed to fix the maximum number of threads for processing a particular request.

2013年7月9日 星期二

撰寫一個簡單的Windows Service

前言


有時候我們會再Server上撰寫一些簡單的小程式,
可能是會定時執行某些任務,但其實不需要任何的畫面顯示,
那我們可以選擇使用Windows Service的方式來運行這些程式,
現在就來介紹如何撰寫一個簡單的Windows Service

實際演練


首先,先建立一個Windows Service的Project
34
我們可以看到Project的結構長得像這樣
35
之後進入ServiceSample的Design Mode,並在上面點選滑鼠右鍵,選擇Properties
設定服務的名字及其他屬性
36
在ServiceSample.cs上點選滑鼠右鍵,選擇View Code,撰寫服務的程式碼內容
01public partial class ServiceSample : ServiceBase
02{
03    public ServiceSample()
04    {
05        InitializeComponent();
06    }
07 
08    protected override void OnStart(string[] args)
09    {
10        Logger.Write("MyServiceSample is Start!");
11    }
12 
13    protected override void OnStop()
14    {
15        Logger.Write("MyServiceSample is Stop!");
16    }       
17}



可以在這邊撰寫服務開始和結束時所要進行的動作,
除之此外還可以override OnPause及OnContinue來撰寫暫停和繼續所要執行的動作,
但要記得將CanPauseAndContinue屬性設為True才能執行暫停和繼續。
接下來再進入ServiceSample的Design Mode,點選滑鼠右鍵,選擇Add Installer
37
可看到畫面上出現serviceProcessInstaller1以及serviceInstaller1,
設定ServiceInstaller1屬性Name為MySampleService,StartType為Auto
設定ServiceProcessInstaller1屬性Account為Local System 本機帳號)
39 40
接下來要進行將程式安裝至Windows的服務之中,打開Visual Studio Tools的Visual Studio 2008 Command Prompt
輸入 installutil 程式所在目錄\WindowsServiceSample.exe,執行成功會看到畫面如下



41
之後打開控制台=>系統管理工具=>服務,可以看到服務已經被成功的安裝
42
接下來再點選滑鼠右鍵,選擇啟動,把正式的把服務運行起來
43
若要移除服務,一樣的只需要在Commad Prompt輸入
installutil /u 程式所在目錄\WindowsServiceSample.exe,成功畫面如下
44
即可解除安裝。

結語


使用Windows服務可以避免用戶不小心關閉程式而導致錯誤的發生,
也可以當作Daemon運行的一種選擇,依據使用上的需求而選擇囉!
如果有任何問題,歡迎大家多多指教 :)