C#多线程实现方法——线程池(Thread Pool)

Posted Avatarx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#多线程实现方法——线程池(Thread Pool)相关的知识,希望对你有一定的参考价值。

 

 

ThreadPool使用

需要定义waitcallback委托形式如

[csharp] view plain copy
 
print?
  1. public delegate void WaitCallback(object state);  
public delegate void WaitCallback(object state);

例如如下例子:

 

[csharp] view plain copy
 
print?
  1. static private void ThreadWorker(object state)  
  2. {  
  3.     int num = (int)state;  
  4.     if (num > 0)  
  5.     {  
  6.         for (int i = 0; i < 3; i++)  
  7.         {  
  8.             Thread.Sleep(1000);// sleep for one second  
  9.             num++;  
  10.             Console.WriteLine(string.Format("Current thread {0} deal with {1}.", Thread.CurrentThread.ManagedThreadId, num));  
  11.         }  
  12.     }  
  13.     return;  
  14. }  
        static private void ThreadWorker(object state)
        {
            int num = (int)state;
            if (num > 0)
            {
                for (int i = 0; i < 3; i++)
                {
                    Thread.Sleep(1000);// sleep for one second
                    num++;
                    Console.WriteLine(string.Format("Current thread {0} deal with {1}.", Thread.CurrentThread.ManagedThreadId, num));
                }
            }
            return;
        }

就是一个简单的隔一秒增加一的自增器。

 

调用ThreadPool的方法很简单,需要引用System.Threading。接着使用QueueUserWorkItem或者UnsafeQueueUserWorkerItem。

[csharp] view plain copy
 
print?
  1. public  static  class  Th read Pool  {   
  2. }   
  3. public  static  bool  QueueUserWorkltem (WaitCa llback  ca llBack) ;   
  4. public  stat ic  bool  QueueUserWork ltem (   
  5. WaitCallback  callBack,   
  6. object  state   
  7. ) ;   
  8. [Secu rityPermi ssion (Secu rityAction .Lin kDemand,  Flags=   
  9. Secu rityPermi ssionFlag . ControlPol icy l   
  10. Secu rityPermi ssionF lag . Cont rolEvidence )]   
  11. public  stat ic  bool  UnsafeQueueUserWorkltem (   
  12. WaitCallback  callBac k,   
  13. obj ect  state   
  14. ) ;   
public  static  class  Th read Pool  { 
} 
public  static  bool  QueueUserWorkltem (WaitCa llback  ca llBack) ; 
public  stat ic  bool  QueueUserWork ltem ( 
WaitCallback  callBack, 
object  state 
) ; 
[Secu rityPermi ssion (Secu rityAction .Lin kDemand,  Flags= 
Secu rityPermi ssionFlag . ControlPol icy l 
Secu rityPermi ssionF lag . Cont rolEvidence )] 
public  stat ic  bool  UnsafeQueueUserWorkltem ( 
WaitCallback  callBac k, 
obj ect  state 
) ; 

可以接受一个参数传递给异步线程。

 

结合前面的自增器,下面例子是申请5个异步线程来运行上述的自增器,传递的值是10,20,30,40,50。接着等待足够长的时间来等待所有线程池里的工作完成。

[csharp] view plain copy
 
print?
  1. for (int i = 1; i <= 5; i++)  
  2.                 ThreadPool.QueueUserWorkItem(ThreadWorker, i * 10);  
  3.             for (int i = 0; i < 5; i++)  
  4.                 Thread.Sleep(1000);// wait for all threads finish  
for (int i = 1; i <= 5; i++)
                ThreadPool.QueueUserWorkItem(ThreadWorker, i * 10);
            for (int i = 0; i < 5; i++)
                Thread.Sleep(1000);// wait for all threads finish

从上面可以看到线程池的使用非常的简单,只需要提供符合waitcallback的委托的工作函数,接着调用QueueUserWorkItem或者UnsafeQueueUserWorkItem添加到线程池里就ok了。但是线程池的缺点也是有的,首先,没有简单的方法来知道工作线程已经完成了,不想asynchronous中提供那么多种等待异步结束的方法,更谈不上能够添加回调函数,也不想thread那样可以使用join函数来等待线程或者使用abort函数中断正在运行的工作项。同时由于它是后台线程,也就是意味如果没有前台线程,程序就关闭,所有正在运行的或者还留在线程池里的工作项就给强制中断。所以不能将一些类似保存退出的操作放进线程池里,因为没有保证机制能够证明所有的线程池里正在运行或者还没运行的工作项能够具有明确的行为。

 

同步机制

ThreadPool没有提供简单的方法来获取工作线程已经结束,所以需要通过事件或者其他的内核对象来实现同步机制。

1. 如果需要获取某一个工作线程是否结束可以使用手动事件来实现。

[csharp] view plain copy
 
print?
  1. static private ManualResetEvent finish = new ManualResetEvent(false);  
  2.         static private void ThreadWorker(object state)  
  3.         {  
  4.               
  5.             {  
  6.                 for (int i = 0; i < 5; i++)  
  7.                 {  
  8.                     Thread.Sleep(1000);  
  9.                     Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i + 1);  
  10.                 }  
  11.                 finish.Set();  
  12.             }  
  13.             return;  
  14.         }  
static private ManualResetEvent finish = new ManualResetEvent(false);
        static private void ThreadWorker(object state)
        {
            
            {
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i + 1);
                }
                finish.Set();
            }
            return;
        }

主调函数实现代码为:

 

[csharp] view plain copy
 
print?
  1.             ThreadPool.QueueUserWorkItem(ThreadWorker);  
  2.             finish.WaitOne();  
  3. finish.Close();  
            ThreadPool.QueueUserWorkItem(ThreadWorker);
            finish.WaitOne();
finish.Close();

或者使用匿名委托函数加using关键字保证了事件的释放

 

[csharp] view plain copy
 
print?
  1. using (ManualResetEvent finish = new ManualResetEvent(false))  
  2. {  
  3.     ThreadPool.QueueUserWorkItem(delegate {  
  4.         for (int i = 0; i < 5; i++)  
  5.         {  
  6.             Thread.Sleep(1000);  
  7.             Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i + 1);  
  8.         }  
  9.         finish.Set();  
  10.     });  
  11.     finish.WaitOne();  
  12. }  
            using (ManualResetEvent finish = new ManualResetEvent(false))
            {
                ThreadPool.QueueUserWorkItem(delegate {
                    for (int i = 0; i < 5; i++)
                    {
                        Thread.Sleep(1000);
                        Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i + 1);
                    }
                    finish.Set();
                });
                finish.WaitOne();
            }

 

 

2.如果需要获取多个工作线程是否结束可以通过最后一个工作线程来触发事件来实现。

如果使用前面的方法给每一个工作线程分配一个事件,然后等待所有事件给触发,是非常低效的,可以使用如下方法

[csharp] view plain copy
 
print?
  1. using (ManualResetEvent finish = new ManualResetEvent(false))  
  2. {  
  3.     int nTask = 10;  
  4.     for (int i = 0; i < nTask; i++)  
  5.     {  
  6.         ThreadPool.QueueUserWorkItem(delegate  
  7.         {  
  8.             for (int j = 0; j < 3; j++)  
  9.             {  
  10.                 Thread.Sleep(1000);  
  11.                 Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i * 10 + j + 1);  
  12.             }  
  13.             if (Interlocked.Decrement(ref nTask) == 0)  
  14.             {  
  15.                 finish.Set();  
  16.             }  
  17.         }, i);  
  18.     }  
  19.     finish.WaitOne();  
  20. }  
            using (ManualResetEvent finish = new ManualResetEvent(false))
            {
                int nTask = 10;
                for (int i = 0; i < nTask; i++)
                {
                    ThreadPool.QueueUserWorkItem(delegate
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            Thread.Sleep(1000);
                            Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i * 10 + j + 1);
                        }
                        if (Interlocked.Decrement(ref nTask) == 0)
                        {
                            finish.Set();
                        }
                    }, i);
                }
                finish.WaitOne();
            }

不过注意由于使用过了匿名委托所以i的值是不确定,有可能是1或者是10,所以为了改变这种情况不能使用匿名委托,

 

[csharp] view plain copy
 
print?
  1. static private int _num = 10;  
  2. static private ManualResetEvent finish = new ManualResetEvent(false);  
  3. static private void ThreadWork(object state)  
  4. {  
  5.     int i = (int)state;  
  6.     for (int j = 0; j < 3; j++)  
  7.     {  
  8.         Thread.Sleep(1000);  
  9.         Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i * 10 + j + 1);  
  10.     }  
  11.     if (Interlocked.Decrement(ref _num) == 0)  
  12.         finish.Set();  
  13. }  
        static private int _num = 10;
        static private ManualResetEvent finish = new ManualResetEvent(false);
        static private void ThreadWork(object state)
        {
            int i = (int)state;
            for (int j = 0; j < 3; j++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("current thead {0} wait for {1} seconds.", Thread.CurrentThread.ManagedThreadId, i * 10 + j + 1);
            }
            if (Interlocked.Decrement(ref _num) == 0)
                finish.Set();
        }

主调函数实现代码为:

 

[csharp] view plain copy
 
print?
  1. for (int i = 0; i < _num; i++)  
  2. {  
  3.     ThreadPool.QueueUserWorkItem(ThreadWork, i);  
  4. }  
  5. finish.WaitOne();  
  6. finish.Close();  

以上是关于C#多线程实现方法——线程池(Thread Pool)的主要内容,如果未能解决你的问题,请参考以下文章

nginx源码分析——线程池

4-5 《Java中多线程重点》——继承Thread实现Runnable死锁线程池Lambda表达式

c# 怎么等待线程池中所有线程都运行结束在运行主线程

C# 显式创建线程 or 使用线程池线程--new Thread() or ThreadPool.QueueUserWorkItem()

多线程之线程池技术实现原理,这篇就够了

Java多线程系列:线程池的实现原理优点与风险以及四种线程池实现