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类:
第一次在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;
}
}
}
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);
}
}
}
}
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);
}
}
}
}
如果文中有什么不当,欢迎拍拍.