多线程工作请求的同步

来源:互联网 发布:excel数据透视表的作用 编辑:程序博客网 时间:2024/05/19 22:50

本文用代码说明如何将不同线程上的工作请求进行缓存并同步到同一个线程上,其目主要有两个:

  1. 减少并发线程,节省系统管理线程的资源消耗。
  2. 避免对共享数据加锁,简化后续处理工作的程序逻辑。
在开发服务器的时候经常会遇到客户端从不同的线程上向服务器发出工作请求的情况,根据业务规则的要求,通常都会对共享的数据进行读写,这就需要对进行线程访问的数据加锁,但加锁会在很大程度上增加程序的复杂性,并且影响工作效率,在处理不当的情况下还会死锁。
 
另外,对客户请求的处理很可能是比较复杂和耗时的,如果这些处理都在客户请求的那个线程上进行,当并发客户比较多时,服务器中运行的线程也会随客户的增加而增加,这会极大地影响服务器的扩展性,所以最好的办法是把客户请求放在一个缓存里同时尽快结束客户请求的线程。
 
针对以上情况,我们在项目开发时采用了缓存加同步的方法,效果显著。具体方法是把不同线程上收到的客户请求放到一个队列中进行缓存并把它们全部同步到同一个线程上来,这样一来就迅速地释放了客户请求线程,同时也避免了对共享数据加锁。
 
本文的代码包括一个Console程序和两个用来进行多线程同步的已经用Generic封装好的两个类,我们最初的应用是在Socket服务器中,工作请求的数据类型是byte[],下边的代码是把实际项目中的程序用Generic包装成两个类,可以用于各种工作请求,使用者可以在不做任何修改的情况下使用在自己的程序中。
下图是Console程序的运行结果,可以看出在线程4,5,6中产生的工作请求全部被同步到同一个线程(线程3)上了.

Console程序运行结果  

以下是Console程序的源代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ThreadUtility
{
    
///<summary>
    
/// Console是测试和演示,它表明从多个线程上产生的工作请求已经被同步到同一个线程上。
    
/// 真正进行同步工作的是它所使用的两个类:WorkItemSynchronizer 和 WrkItemBuffer,
    
/// 其中工作请求的同步线程在 WorkItemSynchronizer 类中。    
    
///</summary>

    class Program
    
{
        
private static bool _stop = false;

        
/// <summary>
        
/// 定义一个将多线程 byte[] 工作请求同步到同一线程的 "WorkItemSynchronizer" 变量。
        
/// </summary>

        private static WorkItemSynchronizer<byte[]> _workItemSynchronizer;


        
static void Main(string[] args)
        
{
            _workItemSynchronizer 
= new WorkItemSynchronizer<byte[]>(ProcessWorkItem);
            _workItemSynchronizer.Start();

            Console.WriteLine(
"按Enter键停止.");
            
for (int i = 0; i < 3; i++)
            
{
                System.Threading.Thread t 
= new System.Threading.Thread(new System.Threading.ThreadStart(CreateWorkItem));
                t.Start();
            }


            Console.ReadLine();
            _stop 
= true;
            _workItemSynchronizer.Stop();

            Console.ReadLine();

        }


        
/// <summary>
        
/// 它产生工作请求, 从多个线程上被调用.
        
/// </summary>

        static void CreateWorkItem()
        
{
            
while (!_stop)
            
{
                
byte[] data = new byte[100];

                System.Console.WriteLine(
string.Format("工作请求({1})在线程 {0} 上产生.",
                    System.Threading.Thread.CurrentThread.ManagedThreadId,
                    data.GetHashCode()));

                _workItemSynchronizer.AcceptWorkItem(data);

                System.Threading.Thread.Sleep(
500);
            }

        }


        
        
/// <summary>
        
/// 在此方法中对各个线成产生的工作请求进行处理,它工作在同步以后的唯一一个线程上.
        
/// </summary>
        
/// <param name="data"></param>

        static void ProcessWorkItem(byte[] data)
        
{
            System.Console.WriteLine(
string.Format("工作请求({2}) 在线程 {1} 上处理, 数据长度 = {0}.",
                data.Length,
                System.Threading.Thread.CurrentThread.ManagedThreadId,
                data.GetHashCode()));
        }

    }

}

以下的两个类 WorkItemSynchronizer<T> 和 WorkItemBuffer<T> 使用者可以在不做任何修改的情况下使用在自己的程序中。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ThreadUtility
{
    
///定义一个处理工作请求的委托
    internal delegate void ProcessWorkItemDelegate<T>(T workItem);

    
///<summary>
    
/// 同步多线程的主要类,它接收来自多线程的工作请求,把它们储存在一个队列里,
    
/// 然后把它们同步到同一个线程中交给处理工作请求的委托.
    
///</summary>

    internal class WorkItemSynchronizer<T>
    
{

        WorkItemBuffer
<T> _workItemBuffer = new WorkItemBuffer<T>();
        
private ManualResetEvent _isStopping = new ManualResetEvent(false);
        ProcessWorkItemDelegate
<T> _workItemProcessor;


        
public WorkItemSynchronizer(ProcessWorkItemDelegate<T> workItemProcessor)
        
{
            
if (workItemProcessor == null)
                
throw new ArgumentNullException("workItemProcessor");

            _workItemProcessor 
= workItemProcessor;
        }


        
///<summary>
        
/// 它接收来自多线程的工作请求,把它们储存在一个队列里,
        
/// 调用此方法的线程在工作请求被处理之前就迅速返回.
        
///</summary>
        
///<param name="workItem"></param>

        public void AcceptWorkItem(T workItem)
        
{
            _workItemBuffer.AddWorkItem(workItem);
        }


        
///<summary>
        
/// 启动进行工作请求同步的线程.
        
///</summary>

        public void Start()
        
{
            _isStopping.Reset();
            _workItemBuffer.Start();

            
//产生一个工作请求的同步线程.
            Thread t = new Thread(new ThreadStart(SynchronizeWorkItem));
            t.Start();
        }


        
///<summary>
        
/// 将工作请求同步到同一线程上.
        
///</summary>

        private void SynchronizeWorkItem()
        
{
            
bool isWorItemAvailable = false;

            
while (!_isStopping.WaitOne(0false))
            
{
                T workItem 
= _workItemBuffer.GetWorkItem(out isWorItemAvailable);

                
if (isWorItemAvailable)
                
{
                    _workItemProcessor(workItem);
                }

            }

        }


        
///<summary>
        
/// 停止工作请求的同步线程.
        
///</summary>

        public void Stop()
        
{
            _isStopping.Set();
            _workItemBuffer.Stop();
        }


    }

}


namespace ThreadUtility
{
    
///<summary>
    
/// 用来缓存工作请求的类. 其中 "AddWorkItem" 方法会被从多个线程上 
    
/// 调用从而把工作请求加到缓存队列中. 
    
/// "GetWorkItem" 被从同步线程调用以取出一个工作请求. 
    
///</summary>
    
///<typeparam name="T"></typeparam>

    internal class WorkItemBuffer<T>
    
{
        
private object _synObject = new object();
        
private System.Collections.Generic.Queue<T> _workItemQueue = new Queue<T>();
        
private ManualResetEvent _isWorkItemAddedToQueue = new ManualResetEvent(false);
        
private ManualResetEvent _isStopping = new ManualResetEvent(false);

        
public WorkItemBuffer() { }
        
public int Count
        
{
            
get
            
{
                
return _workItemQueue.Count;
            }

        }


        
public void Start()
        
{
            
lock (_synObject)
            
{
                _workItemQueue.Clear();
            }


            _isStopping.Reset();
        }


        
public void Stop()
        
{
            _isStopping.Set();
            _isWorkItemAddedToQueue.Set();
        }


        
///<summary>
        
/// 该方法会被从多个线程上调用从而把工作请求加到缓存队列中.
        
///</summary>
        
///<param name="workItem"></param>

        public void AddWorkItem(T workItem)
        
{
            
lock (_synObject)
            
{
                _workItemQueue.Enqueue(workItem);
            }


            
//通知同步线程队列中已经有工作请求加入,
            _isWorkItemAddedToQueue.Set();
        }


        
///<summary>
        
/// 它被从同步线程调用以取出一个工作请求.
        
/// 当队列中没有工作请求是该方法会被阻塞在ManualResetEvent.WaitOne上.
        
///</summary>
        
///<param name="isWorItemAvailable">它指明是否有一个工作请求返回..</param>
        
///<returns>返回队列中第一个工作请求.</returns>

        public T GetWorkItem(out bool isWorItemAvailable)
        
{
            
//当队列中没有工作请求是该方法会被阻塞在这个语句上,
            _isWorkItemAddedToQueue.WaitOne();

            
if (_isStopping.WaitOne(0false))
            
{
                isWorItemAvailable 
= false;
                
return default(T);
            }


            
lock (_synObject)
            
{
                
if (_workItemQueue.Count > 0)
                
{
                    isWorItemAvailable 
= true;
                    
return _workItemQueue.Dequeue();
                }

                
else
                
{
                    isWorItemAvailable 
= false;

                    
//当队列空时重新设置信号来阻塞下一次工作请求的读取.
                    _isWorkItemAddedToQueue.Reset();

                    
return default(T);
                }

            }

        }

    }

}