5555...就要hashtable 多线程安全嘛..

先寒一下自己的标题...有感于<555 不嘛 !我就要 返回值重载 ! >

第一次在Cnblog发文章,很不幸,在文章要完成居然出现了...Internet Exlpore执行了非法操作。.我靠.

起因:
我在实现Xxmm3(一个O/R)的Cache部份时需要一个可多线读写的hashtable。找了一下资料,木找到,如果各位有资料的话,给我提个醒,谢谢。
看了msdn的 Hashtable.Synchronized;
通过集合枚举在本质上不是一个线程安全的过程。甚至在对集合进行同步处理时,其他线程仍可以修改该集合,这会导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

也就是说,我仍然无法实现多线程安全的读写(在枚举过程,仍然无法改写hashtable)

解决:
为了高效的实现目标,多线程安全的读写hashtable,需要解决三个问题
1。在开始一个或多个枚举时,禁止对hashtable的改写
2。在改写ht,但改写未结束时,不能再开始一个新的枚举。
3。允许多个枚举并发

为了解决1,在开始枚举时,需要设置一个标志m_CanWrite 禁止改写操作。
为了解决2,在改写ht的时,不能设置一个flag m_IsWrite从儿禁止/允许一个新枚举的开始
为了解决3,需要一个枚举计数器,知道最后一个枚举结束时,才能允许改写ht.

一个SafeHashtable类:
using System;
using System.Collections;
using System.Threading;

namespace ConsoleApplication1
{
    
/// <summary>
    
/// SafeHashtable 的摘要说明。
    
/// </summary>
    public class SafeHashtable
    {
        
private Hashtable ht = new Hashtable();
        
        
private volatile bool m_CanWrite = true;
        
private volatile bool m_IsWrite = false;
        
private volatile int m_EnumerateCount = 0;

        
public SafeHashtable()
        {

        }

        
private void SafeWait()
        {
            
//如果hastable不允许写
            
//也就是一个枚举正在进行或另一个写如操作正在发生时
            
//线程等待知道 m_CanWrite为true
            while(!m_CanWrite)
            {
                Thread.Sleep(
10);
            }
        }

        
public void Add(object key,object v)
        {
            
//当Add操作发生时但未结束时
            
//要保证Remove操作不会不会将m_IsWrite写成true
            
//Remove操作也是一样的道理
            this.SafeWait();
            
this.m_CanWrite = false;
            
this.m_IsWrite = true;
            ht.Add(key,v);
            
this.m_IsWrite = false;
            
this.m_CanWrite = true;
        }

        
public void Remove(object key)
        {
            
this.SafeWait();
            
this.m_CanWrite = false;
            
this.m_IsWrite = true;
            ht.Remove(key);
            
this.m_IsWrite = false;
            
this.m_CanWrite = true;
        }
        
        
public Hashtable StartEnumerate()
        {
            
//等带写操作结束
            while(m_IsWrite)
            {
                Thread.Sleep(
10);
            }
            
this.m_CanWrite = false;
            
this.m_EnumerateCount ++;
            Hashtable eht 
= Hashtable.Synchronized(ht);
            
            
return eht;
        }

        
public void EndEnumerate()
        {
            
this.m_EnumerateCount --;
            
//禁止再开始一个枚举
            this.m_IsWrite = true;
            
//如果这是最后一个枚举
            
//则允许hashtable可写入
            if (this.m_EnumerateCount <= 0)
            {
                
this.m_CanWrite = true;
            }
            
//允许再开始一个枚举
            this.m_IsWrite = false;
        }
    
    }
}


当然你完全可以从让Safehashtable从hashtable继承而来,因为cache操作频繁,我不想使用override
为什么要使用:while(..){sleep()}
我测试了一下,如果不使用Sleep(...),而让while循环体为空的话,十分占用cpu,测试的时候达到了100%.还是Sleep要省一些资源。
需要开始一个枚举的时候,你需要调用 StartEnumerate()枚举结束时,要记得掉用:EndEnumerate这是一个成对操作.否则其他线程就无法再改写ht了。

下面是测试代码:

using System;
using System.Collections;
using System.Threading;

namespace ConsoleApplication1
{
    
/// <summary>
    
/// Class1 的摘要说明。
    
/// </summary>
    class Class1
    {
        
//static Hashtable ht = new Hashtable();
        static SafeHashtable ht = new SafeHashtable();
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>
        
        [STAThread]
        
static void Main(string[] args)
        {
            
for (int j = 1;j <= 3;j++)
            {
                
for (int i = 0;i < 10000;i ++)
                {
                    ht.Add(j
*1000000 + i,i*100);
                }

                ThreadStart myThreadDelegate 
= new ThreadStart(Class1.FreeSpac);
                Thread myThread 
= new Thread(myThreadDelegate);
                myThread.Name 
= "000";
                myThread.Start();
                myThread 
= new Thread(myThreadDelegate);
                myThread.Name 
= "999";
                myThread.Start();
                Write(j);
            }
            Console.ReadLine();

        }

        
static void FreeSpac()
        {
            Hashtable safeHt 
= ht.StartEnumerate();
            
//lock(typeof(Class1))
            {
                
//ht.CanWrite = false;
                foreach(DictionaryEntry entry in safeHt)
                {
                    Console.WriteLine(
"Thread[{2}] Key[{0}] Value[{1}]",entry.Key,entry.Value,Thread.CurrentThread.Name);
                }
                ht.EndEnumerate();
            }
            
        }

        
static void Write(int j)
        {
            Thread.Sleep(
100);
            
for (int i = 10000;i < 20000;i++)
            {
                ht.Add((i 
+ j*10000000).ToString() + "*",DateTime.Now);
            }
        }
    }
}

如果文中有什么不当,欢迎拍拍.
 



 

posted on 2005-11-05 14:15  噢,酷哥  阅读(3201)  评论(15编辑  收藏  举报

导航