using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;
using System.IO;namespace 多线程写入
{
    public partial class Form1 : Form
    {        private Dictionary<string, string> stcMem = new Dictionary<string, string>();//用于多线程存入数据
        //private  Hashtable hs = new Hashtable();
        //private  Hashtable hssync = Hashtable.Synchronized(new Hashtable());
        bool canWrite = true;
        int threadNum = 0;//已启动的线程条数
        ManualResetEvent writeEvent = new ManualResetEvent(true);
        int writeNum = 0;//写入数量
        int readnum = 0;//从对象读出线程条数        int allNum = 0;//写入数据累加        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        ///执行写入操作
        /// </summary>
        /// <param name="i"></param>
        private void m_write(object i)
        {            threadNum++;            while (true)
            {
                if (canWrite)//多此一举吗?因为我在计时器里 writeEvent.Reset();并不能阻止写入,所以又加了这个
                {
                    //
                }
                else
                {
                    writeEvent.Reset();
                }                writeEvent.WaitOne();//这个好象并不能锁住写放啊???????到一定时间,阻塞就会失败!!!!!!为什么,就会报“集合已修改;可能无法执行枚举操作。这里老是会出这个错误” 请高手解
                stcMem.Add(Guid.NewGuid().ToString(), "valuevaluevaluevaluevaluevaluevaluevaluevaluevaluevalue");
                //hssync.Add(Guid.NewGuid().ToString(), "valuevaluevaluevaluevaluevaluevaluevaluevaluevaluevalue");
                writeNum++;
                Thread.Sleep(100);            }
        }        private void timerShow_Tick(object sender, EventArgs e)
        {
            lbNum.Text = "线程数:" + threadNum + " 执行写入:" + writeNum.ToString() + "  实际效果:" + allNum + "当前条数:" + stcMem.Count + " 读取" + readnum + "次";
            //lbNum.Text = "线程数:" + threadNum + " 执行写入:" + writeNum.ToString() + "  实际效果:" + hssync.Count;        }        private void button1_Click(object sender, EventArgs e)
        {            for (int i = 0; i < 100; i++)//创建100个线程进行不段的写入操作
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(m_write), i);            }
        }        private void button2_Click(object sender, EventArgs e)
        {
            canWrite = false;
            writeEvent.Reset();//停止写入
        }        private void timerSave_Tick(object sender, EventArgs e)
        {
            canWrite = false;
            writeEvent.Reset();
            System.Text.StringBuilder sb = new StringBuilder();
            foreach (var i in stcMem)//集合已修改;可能无法执行枚举操作。这里老是会出这个错误
            {
                string val = i.Value;
                if (!string.IsNullOrEmpty(val))
                sb.Append(i.Value.ToString() + "\n");
            }
            allNum += stcMem.Count;
            stcMem.Clear();
            string filename = @"savefiles/" + Guid.NewGuid().ToString() + "_" + readnum + ".txt";
            string willstr = sb.ToString();
            if (willstr != "")
            {
                File.WriteAllText(filename, willstr);
            }
            readnum++;            //IDictionaryEnumerator myEnumerator = hssync .GetEnumerator();
            //while (myEnumerator.MoveNext())
            //{
            //    string val = myEnumerator.Value.ToString();
            //}
            canWrite = true;
            writeEvent.Set();
        }
    }
}

解决方案 »

  1.   

    直接用lock就行了private void m_write(object i)
      {  threadNum++;  while (true)
      {  
    lock(stcMem){  
    stcMem.Add(Guid.NewGuid().ToString(), "valuevaluevaluevaluevaluevaluevaluevaluevaluevaluevalue");
      //hssync.Add(Guid.NewGuid().ToString(), "valuevaluevaluevaluevaluevaluevaluevaluevaluevaluevalue");
      writeNum++;
    }
      Thread.Sleep(100);  }
      }private void timerSave_Tick(object sender, EventArgs e)
      {
      System.Text.StringBuilder sb = new StringBuilder();
    lock(stcMem)
    {
      foreach (var i in stcMem)//集合已修改;可能无法执行枚举操作。这里老是会出这个错误
      {
      string val = i.Value;
      if (!string.IsNullOrEmpty(val))
      sb.Append(i.Value.ToString() + "\n");
      }
      allNum += stcMem.Count;
      stcMem.Clear();
    }
      string filename = @"savefiles/" + Guid.NewGuid().ToString() + "_" + readnum + ".txt";
      string willstr = sb.ToString();
      if (willstr != "")
      {
      File.WriteAllText(filename, willstr);
      }
      readnum++;  //IDictionaryEnumerator myEnumerator = hssync .GetEnumerator();
      //while (myEnumerator.MoveNext())
      //{
      // string val = myEnumerator.Value.ToString();
      //}
      canWrite = true;
      writeEvent.Set();
      }
      }
      

  2.   

    lock 我用过了,效率非常的低下,我只想在定时读取时这个时间内阻止写入,但不在读取时,希望并发写入,如果用lock , 那只能是单线程写入,其它线“写放线程”排队,非常的慢。
    我想实现的是,平常情况下,高效的多线程并发写入,在读取dictionary时,才暂时阻止所有线程写入,一旦读完,马上重新允许写入
    上面的程序在线程数不大的情况下(50-60个写入线程),执行段时间就会出现“集合已修改;可能无法执行枚举操作。”,我也想用for ,便那样,是边读边写,数据会不数据会不符合要求,请高手解答!谢谢
      

  3.   

    比如接收socket 异步数据
      

  4.   

    问题很明显
    如果你调用writeEvent.ReSet()的时候有的线程已经越过了writeEvent.WaitOne(),将要或者正在执行stcMem.Add(),那么就很有可能在你获得了迭代器之后会因为stcMem.Add()的执行导致集合状态改变。
    也就是说你仅仅向写入方发出了暂停写入的请求,但是没有等待已经开始的写入完成。换成ConcurrentDictionary可以解决这个问题——它的迭代器本身就是可以安全同步读写的,writeEvent这些都可以省掉。
      

  5.   

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Windows.Forms;namespace WindowsFormsApplication5
    {
        public partial class Form1 : Form
        {
            private ReaderWriterLockSlim _ReadWriteLock;
            private Dictionary<string, string> stcMem = new Dictionary<string, string>(1000000);//用于多线程存入数据        public Form1()
            {
                InitializeComponent();
                Control.CheckForIllegalCrossThreadCalls = false;
                _ReadWriteLock = new ReaderWriterLockSlim();
            }        private void WriteThread(Object obj)
            {
                _ReadWriteLock.EnterWriteLock();
                try
                {
                    for (Int32 i = 0; i < 10000; i++)
                    {
                        stcMem.Add(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
                    }
                }
                finally
                {
                    _ReadWriteLock.ExitWriteLock();
                }
            }        private void ReadThread(Object obj)
            {
                _ReadWriteLock.EnterReadLock();
                try
                {
                    foreach (KeyValuePair<String, String> item in stcMem)
                    {
                        listBox1.Items.Add(item.Key + " " + item.Value); 
                    }
                }
                finally
                {
                    _ReadWriteLock.ExitReadLock();
                }
            }        private void button1_Click(object sender, EventArgs e)
            {
                for (Int32 i = 0; i < 10; i++)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(WriteThread), null);
                }
            }        private void button2_Click(object sender, EventArgs e)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(ReadThread), null);
            }    }
    }