现在我自己的解决办法是将全局变量 先赋值给局部变量 然后才传入多线程 就没有问题 不知道这个方法是否最好?

解决方案 »

  1.   

    伪代码
    Thread[] trd = new Thread[20];//线程数组
    public int trd_cnt=-1//公共变量private void Submit_Click(object sender, EventArgs e)
    {
    trd_cnt++;
      thr[trd_cnt]= new Thread(new ThreadStart(delegate { ThreadTask(trd_cnt, StrSN, SeleProd, event_time); }));
    }private void ThreadTask(int list_no, string StrSN, string SeleProd,DateTime event_time)
            {//do something}
      

  2.   

    当连续Click 传入的trd_cnt会出现重复的value
    修改后
    先赋值
    list_no=trd_cnt 
    然后传入list_nothr[list_no]= new Thread(new ThreadStart(delegate { ThreadTask(list_no, StrSN, SeleProd, event_time); })); 
      

  3.   

    new ThreadStart(delegate { ThreadTask(trd_cnt, StrSN, SeleProd, event_time); }) 这个有个闭包操作,闭包里面使用的变量如果是全局变量的话会产生 闭包内的相应参数会在运行时才去取这个全局变量(我是按照js的闭包效应来解释的,C#的闭包我基本没用过, 在js中如果你用for去给若干个控件注册事件的话,用for中的循环变量i作为参数 你会发现到最后 你注册的所有事件的参数都是i 最后运行的值 也就是for的最大值)
      

  4.   

    9楼和14楼已经提出了不错的意见。1. 线程加载需要时间,这个可能造成你的数值重复。
    2. 建议将线程执行函数单独定义。
    3. 多线程访问同一个全局变量要加锁。
    4. 既然是一个全局变量,你没必要将这个变量通过参数传递。
    5. 建议你将读取和修改trd_cnt数值做成两个方法,其中修改trd_cnt的方法要对trd_cnt进行加锁。
      

  5.   

    EnoughPoint: 加锁会造成线程效率下降 ,所以使用委托传递参数的意义 就是希望通过委托将这个公共参数在主线程里面锁定 然后再传递到子线程 。是不是说委托传递的参数还只是传递一个地址 而并不是参数本身?
      

  6.   

    EnoughPoint: 为什么加锁无效?
    加锁的代码
    Thread[] trd = new Thread[20];//线程数组 
    public int trd_cnt=-1//公共变量 private void Submit_Click(object sender, EventArgs e) 

      lock(object)
          {
           trd_cnt++; 
           }
         thr[trd_cnt]= new Thread(new ThreadStart(delegate { ThreadTask( StrSN, SeleProd, event_time); })); 
    } private void ThreadTask( string StrSN, string SeleProd,DateTime event_time) 
            {lock(object)
                {int list_no=trd_cnt;
                 }
    //do something}
      

  7.   


    LZ,你在线程函数体外加锁,多个线程同时运行时仍然会取到过时的数值,你在线程函数体内加锁,就是把你那段修改trd_cnt的Lock代码加到ThreadTask函数体内。
      

  8.   

    我试了下为什么是正确的?
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;namespace WindowsApplication188
    {
        public partial class Form1 : Form
        {
            ListBox LB = new ListBox();
            
            public Form1()
            {
                InitializeComponent();            LB.Parent =this;            Button B = new Button();
                B.Location = new Point(0, 200);
                B.Parent = this;
                B.Click += new EventHandler(Submit_Click);
            }        Thread[] trd = new Thread[20];//线程数组 
            public int trd_cnt = -1;//公共变量         private void Submit_Click(object sender, EventArgs e)
            {
                trd_cnt++;
                if (trd_cnt > trd.Length - 1)
                    return;
                trd[trd_cnt] = new Thread(new ThreadStart(delegate { ThreadTask(trd_cnt, String.Empty, String.Empty, DateTime.Now); }));
                trd[trd_cnt].Start(); 
            }        private void ThreadTask(int list_no, string StrSN, string SeleProd, DateTime event_time)
            {
                this.Invoke(new Action<String>(DoSetString), new Object[] { list_no .ToString ()});
            }        void DoSetString(String S)
            {
                LB.Items.Add(S);
            }
        }
    }
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    19
      

  9.   


    LZ,你把代码修改为:private void Submit_Click(object sender, EventArgs e)
    {
           lock(object)
                {
                     trd_cnt++;
                   //int list_no=trd_cnt;
                   thr[trd_cnt]= new Thread(new ThreadStart(delegate { ThreadTask(   
                     StrSN, SeleProd, event_time); }));
                }
    }private void ThreadTask( string StrSN, string SeleProd,DateTime event_time)
            {
    //do something
    }另,你与其用数组来存储线程对象,不如用List或Hashtable来替换,线程New出来后,把线程加入到List或Hashtable里面去,然后再启动,这样你就能避免维护trd_cnt这个全局变量。如果你确实要维护这个变量也可以,提个思路:
    List<Thread> threadPool = new List<Thread>();
    DealingFunction()
    {
       Thread tempTrd = new Thread(new ThreadStart(delegate { ThreadTask(   
                     StrSN, SeleProd, event_time); }));
       trd_cnt++; //这个变量在这里确实多次一举
       threadPool.Add(tempTrd);
       tempTrd.start();
    }
      

  10.   

    22# 你的线程函数体执行不是一个很耗时的函数体,你在: void DoSetString(String S)
            {
                for(long i = 0; i < 10000000000000; i++);
                LB.Items.Add(S);
            }
    然后快速单击 submit按钮 试试看。
      

  11.   

    需要知道LZ在ThreadTask中山如何操作trd_cnt的代码 有没有赋值运算?
      

  12.   

            static object o = new object();        private void Submit_Click(object sender, EventArgs e)
            {
                lock (o)
                {
                    trd_cnt++;
                    if (trd_cnt > trd.Length - 1)
                        return;
                    trd[trd_cnt] = new Thread(new ThreadStart(delegate { ThreadTask(trd_cnt, String.Empty, String.Empty, DateTime.Now); }));
                    trd[trd_cnt].Start();
                }
            }
      

  13.   

    我想问楼上几位: 楼主问的问题是 为什么他给委托传入的是 trd_cnt的瞬时值(也就是按第一次应该为1,第二次应该为2) 而为什么 委托函数在运行的时候 实参去变成了 trd_cnt的即时值(也就是更改以后的值)
     也就是他的本意 函数应该实参是:
         ThreadTask(1,String.Empty, String.Empty)
         ThreadTask(2,String.Empty, String.Empty)
         ThreadTask(3,String.Empty, String.Empty)
      但为什么它的第一个参数会根据trd_cnt变动 改成了:
         ThreadTask(3,String.Empty, String.Empty)
         ThreadTask(3,String.Empty, String.Empty)
         ThreadTask(3,String.Empty, String.Empty)
    我想楼主表到了这样一个意思吧, 至于它的线程执行代码有没有耗时 和这个问题我觉得一点关系都没有, 个人感觉就是闭包问题,就像我在13,14楼说到的那样
      

  14.   

    首先 谢谢各位 .天芮说的 (27楼)就是现在的异常
    另外 在线程里面并没有对全局变量进行修改 只是读取。天芮:threadstart 不能接受非匿名委托啊 如果可以的话 你提供一下代码
    EnoughPoint:即使将thread实例lock 还是不能解决这个问题
      

  15.   

    参考MSDN的Thread (ParameterizedThreadStart) 构造函数 自己做一个试试
      

  16.   

    可以在Submit_Click里加2句话,
    运行提示在不同步的代码里调用同步方法
     private void Submit_Click(object sender, EventArgs e)
            {
                 Monitor.Enter(trd_cnt);                trd_cnt++;
                    if (trd_cnt > trd.Length - 1)
                        return;
                    trd[trd_cnt] = new Thread(new ThreadStart(delegate { ThreadTask(trd_cnt, String.Empty, String.Empty, DateTime.Now); }));
                    trd[trd_cnt].Start();             Monitor.Exit(trd_cnt);
            }
      

  17.   

    wartim : 锁定不能解决这个问题
      

  18.   

    天芮: Thread (ParameterizedThreadStart) 构造函数 需要重新封装类 但是子线程里面需要访问form
    控件 有没有简单的用委托的 然后赋值到子线程里面?
      

  19.   


     thr[trd_cnt]= new Thread(new ThreadStart(delegate { ThreadTask(trd_cnt, StrSN, SeleProd, event_time); }));  //改动成以下
      class WorkThreadParam
      {
        public string Parm1;
        public string Parm2;
        public int Parm3;
      }  thr[trd_cnt]= new Thread(new ParameterizedThreadStart(ThreadTask));
      WorkThreadParam newParm = new WorkThreadParam();
      newParm.Parm3 = trd_cnt;
      newParm.Parm1 = string.Empty;
      newParm.Parm2 = string.Empty;  thr[trd_cnt].Start(newParm);  void ThreadTask(object a)
      {
         //在这里会从thr[trd_cnt].Start(newParm)那里接受newParm 它是一个WorkThreadParam类型参数,强行转换过来然后用就可以了
      }
     代码没经过测试 不过基本就这么用,这样可以消除闭包效应 不过有点麻烦,还不如就你上面那种方法 把它赋值给一个临时变量,这个临时变量由于生存期关系它会和你的委托绑定,不会造成那种闭包效应
      

  20.   

    其实你只注意了你的第一个参数,其实如果你的第二个和第三个参数都是全局变量的话(比如控件的属性值) 将来都会早成你当前这个现象,主要问题就出在
    thr[trd_cnt]= new Thread(new ThreadStart(delegate { ThreadTask(trd_cnt, StrSN, SeleProd, event_time); })); 上 这个写法应该是vs2008才能写的,2008也特别提出了闭包的概念,虽然在以前的VS版本上也有闭包用法 但是2008还是第一个比较系统的提出了闭包
      以前像在vs2005 中  int i=1;string a= (i+2+3).ToString() 其实就是一个闭包写法