巧用Dictionary对象实现线程安全类(实例)

     在前一面篇文章[设计安全的多线程应用程序(线程安全)]中,我们讲了,什么是线程安全,列举一些常见的线程安全和非线程安全的情况。还没对线程安全了解的同学请点上面的链接。现在我们来看线程不安全的本质是什么。

   我们来想想在单线程的情况下安全,为什么在多线程的情况下是不安全的呢?无非就是因为多线程是并行执行,当然并行执行本身是没有错的。如果这些并行跑起来的线程,出现下列情况时可能会导致线程不安全。第一,争抢独占的资源如同时写一个文件,对独占的资源的排它锁。第二,程序中的全局对象,如类中对静态成员,被多个线程同时更新(修改或者删除),会产生脏的数据或者是其它线程得不到正确的数据的后果;同一个类实例的非静态成员,被此实例中的函数使用(其中有修改实例成员的功能),此时两个以上线程同时使用时,就可能使类成员的状态对同一个线程的使用的不一致性。

 

     由此,可以看出,线程安全的本质就是,要保护全局对象,在使用时只能被一个线程使用(包括读,写,删除)(注意,对全局对象只限于只读,那么是线程安全的)。解决的方法无非就是两种,第一,把这个对象或者资源锁住,即等我用完了,其它线程才能再能,否则其它线程等待,这也是最常见的方式。第二,给每个线程分配属于只属于自己的"全局对象"(这样听起来好像有点别扭)。

     要注意的一点是,很多语句的类库中的类,都没在实现线程安全,这是因为基于性能的考虑,这样的情况下,就让我们程序员自己来决定实现线程安全。


下面这两种设计线程安全类的例子:

 第一,用lock语句(在c#中)

1,锁住同一个实例中的全局资源(同一实例中非静态成员)

public   void  Function()
{
    System.Object lockThis 
=   new  System.Object();
    
lock (lockThis)
    {
        
//  Access thread-sensitive resources.
    }
}

 

2,锁住类中静态成员

static  StringBuilder CstrBuild  =   new  StringBuilder();
static  System.Object lockThis  =   new  System.Object();
public   void  Function()
{    
    
lock (lockThis)
    {
        CstrBuild.Append(
" test " );
    }
}

 

 第二,使用给每一个线程分配自己的“全局对象”的方法,实现一个带有缓存的写文件功能的线程安全的类(避免每条数据写一次,造成IO瓶颈)。

它的原则是给每个线程分配自己的“全局对象”。

实现功能:

当每个线程的StringBuilder对象,到达一定长度 const int CBufferLenght = 10000*38后才写入文件。 

 

步骤是:

1, 声明一个分局对象集合,我用了Dictionary对象,key是线程id,value就是它的“全局对象”

static  Dictionary < int , StringBuilder >  CLBufferOfEThread  =   new  Dictionary < int , StringBuilder > ();

 

2,需要注意的是,当第一次将数据加入到全局集合对象Dictionary时,需要锁定Dictionary对象

lock  (lockBuffer)
 {
  CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId] 
=   new  StringBuilder(pstrContent);
}

 

 

3, 给Dictionary对象赋值时,无需使用lock(因为它是线程独占的全局对象,不会冲突,我给每个线程id分配了自己的“全局对象”,即集合中的某个元素)

CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].AppendLine(pstrContent);

 

4,在虚构函数处理还未写入文件的数据

~ StreamWriteWithBuffer()
        {
            
foreach  (StringBuilder valus  in  CLBufferOfEThread.Values)
            {
                
if  (valus.Length  >   0   &&  CLimpIDfileName.Length  !=   0 )
                {
                    
using  (StreamWriter logWrite  =   new  StreamWriter(CLimpIDfileName,  true , Encoding.Default))
                    {
                        logWrite.WriteLine(valus.ToString());
                    }
                    valus.Remove(
0 , valus.Length);
                }
            }
        }

 

下面就是具体的代码,

调用代码:

StreamWriteWithBuffer StreamWriteWithBuffer  =   new  StreamWriteWithBuffer();
         ///   <summary>
        
///  Writing content once a content was created
        
///   </summary>
        
///   <param name="pstrLogName"> limpid file name </param>
        
///   <param name="pstrContent"></param>
         public   void  WriteLimpIDLog( string  pstrContent)
        {
            
try
            {

                    StreamWriteWithBuffer.WriteLine( " Stats/ "   +  CLimpIDfileName, pstrContent);
            }
            
catch  (Exception ex)
            {
                Console.WriteLine(
" Exception of log file: "   +  ex.Message);
            }
        }


类实现:


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

namespace Project.Common
{
    
///   <summary>
    
///  the class for writing something into a file,it can have a buffer to store the data which need to write before write to file really.
    
///   </summary>
     class  StreamWriteWithBuffer : IDisposable
    {
        
string  CLimpIDfileName  =   string .Empty;
        
const   int  CBufferLenght  =   10000 * 38 ;        
        
// static StringBuilder CBuffer = new StringBuilder();

        
static  Dictionary < int , StringBuilder >  CLBufferOfEThread  =   new  Dictionary < int , StringBuilder > ();
 
        
public  StreamWriteWithBuffer( string  pfileName)
        {
            CLimpIDfileName 
=  pfileName;
                        
        }
        
public  StreamWriteWithBuffer()
        {
        }

        
~ StreamWriteWithBuffer()
        {
            
foreach  (StringBuilder valus  in  CLBufferOfEThread.Values)
            {
                
if  (valus.Length  >   0   &&  CLimpIDfileName.Length  !=   0 )
                {
                    
using  (StreamWriter logWrite  =   new  StreamWriter(CLimpIDfileName,  true , Encoding.Default))
                    {
                        logWrite.WriteLine(valus.ToString());
                    }
                    valus.Remove(
0 , valus.Length);
                }
            }
        }

        private static Object lockWrite = new Object();

 

        private static Object lockBuffer = new Object();

         public   void  WriteLine( string  pLimpIDfileName,  string  pstrContent)
        {
            
try
            {
                
if  (CLBufferOfEThread.ContainsKey(Thread.CurrentThread.ManagedThreadId))
                {
                    
if  (CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].Length  <  CBufferLenght)
                    {
                        CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].AppendLine(pstrContent);
                        CLimpIDfileName 
=  pLimpIDfileName;
                    }
                    
else
                    {
                        
lock  (lockWrite)
                        {
                            
using  (StreamWriter logWrite  =   new  StreamWriter(pLimpIDfileName,  true , Encoding.Default))
                            {
                                logWrite.WriteLine(CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].ToString());
                            }
                        }

                        CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].Remove(
0 , CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId].Length);
                    }
                }
                
else
                {
                    
lock  (lockBuffer)
                    {
                        CLBufferOfEThread[Thread.CurrentThread.ManagedThreadId] 
=   new  StringBuilder(pstrContent);
                    }
                }
            }
            
catch  (Exception ex)
            {

                Console.WriteLine(
" Exception of limpID log file: "   +  ex);
            }
        }

        
public   void  Dispose()
        {             
        }
    }
}

 

 

总之,线程安全除了在设计时要尽量避免以上的情况之外,还要反复的测试你的类,或者程序,才能更终实现线程安全类。

转载于:https://www.cnblogs.com/luyinghuai/archive/2008/08/29/1279665.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值