.net 多线程委托传入全局变量作为参数 出现参数不正确

meskin 2009-12-01 11:24:17
现在我自己的解决办法是将全局变量 先赋值给局部变量 然后才传入多线程 就没有问题 不知道这个方法是否最好?
...全文
601 37 打赏 收藏 转发到动态 举报
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
meskin 2009-12-03
  • 打赏
  • 举报
回复
结贴 散分 走人
龙宜坡 2009-12-02
  • 打赏
  • 举报
回复
看半天,不明白LZ究竟要达到什么效果!

多线程中使用全局变量肯定要加互斥锁是必然的,

至于参数不正确,怎么个不正确法?

代码贴上来或把异常贴上来看看!

描述清楚点!
tianrui456 2009-12-02
  • 打赏
  • 举报
回复
我想问楼上几位: 楼主问的问题是 为什么他给委托传入的是 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楼说到的那样
wartim 2009-12-02
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 enoughpoint 的回复:]
22# 你的线程函数体执行不是一个很耗时的函数体,你在:

void DoSetString(String S)
        {
           for(long i = 0; i < 10000000000000; i++);
            LB.Items.Add(S);
        }
然后快速单击 submit按钮 试试看。
[/Quote]


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();
}
}
gghlkk 2009-12-02
  • 打赏
  • 举报
回复
需要知道LZ在ThreadTask中山如何操作trd_cnt的代码 有没有赋值运算?
EnoughPoint 2009-12-02
  • 打赏
  • 举报
回复
22# 你的线程函数体执行不是一个很耗时的函数体,你在:

void DoSetString(String S)
{
for(long i = 0; i < 10000000000000; i++);
LB.Items.Add(S);
}
然后快速单击 submit按钮 试试看。
EnoughPoint 2009-12-02
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 meskin 的回复:]
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}
[/Quote]

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();
}
wartim 2009-12-02
  • 打赏
  • 举报
回复
我试了下为什么是正确的?


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
zuojunyuan 2009-12-02
  • 打赏
  • 举报
回复
看看,学习下。
EnoughPoint 2009-12-02
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 meskin 的回复:]
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}
[/Quote]

LZ,你在线程函数体外加锁,多个线程同时运行时仍然会取到过时的数值,你在线程函数体内加锁,就是把你那段修改trd_cnt的Lock代码加到ThreadTask函数体内。
EnoughPoint 2009-12-02
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 meskin 的回复:]
EnoughPoint: 加锁会造成线程效率下降 ,所以使用委托传递参数的意义 就是希望通过委托将这个公共参数在主线程里面锁定 然后再传递到子线程 。是不是说委托传递的参数还只是传递一个地址 而并不是参数本身?
[/Quote]

多线程访问 全局变量,如果你只是读取操作的话,那大可不必加锁,但涉及到修改操作的时候,通常还是要加锁的,否则线程可能取到一个过时的数据。你所说的损失效率,在你现在的应用场景下是不可避免的。
tianrui456 2009-12-02
  • 打赏
  • 举报
回复
其实你只注意了你的第一个参数,其实如果你的第二个和第三个参数都是全局变量的话(比如控件的属性值) 将来都会早成你当前这个现象,主要问题就出在
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() 其实就是一个闭包写法
tianrui456 2009-12-02
  • 打赏
  • 举报
回复

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类型参数,强行转换过来然后用就可以了
}


代码没经过测试 不过基本就这么用,这样可以消除闭包效应 不过有点麻烦,还不如就你上面那种方法 把它赋值给一个临时变量,这个临时变量由于生存期关系它会和你的委托绑定,不会造成那种闭包效应
meskin 2009-12-02
  • 打赏
  • 举报
回复
天芮: Thread (ParameterizedThreadStart) 构造函数 需要重新封装类 但是子线程里面需要访问form
控件 有没有简单的用委托的 然后赋值到子线程里面?
meskin 2009-12-02
  • 打赏
  • 举报
回复
wartim : 锁定不能解决这个问题
wartim 2009-12-02
  • 打赏
  • 举报
回复
可以在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);
}
tianrui456 2009-12-02
  • 打赏
  • 举报
回复
参考MSDN的Thread (ParameterizedThreadStart) 构造函数 自己做一个试试
meskin 2009-12-02
  • 打赏
  • 举报
回复
首先 谢谢各位 .天芮说的 (27楼)就是现在的异常
另外 在线程里面并没有对全局变量进行修改 只是读取。

天芮:threadstart 不能接受非匿名委托啊 如果可以的话 你提供一下代码
EnoughPoint:即使将thread实例lock 还是不能解决这个问题
Justin-Liu 2009-12-01
  • 打赏
  • 举报
回复
多线程全局变量能不用最好不用
mowensky 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 vssvss 的回复:]
把代码发上来 看看
[/Quote]

加载更多回复(16)

111,120

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧