都来分析一下-- --线程间操作无效的原因 昨天刚学Socket,遇到问题,至今没有整明白。请各位高手帮忙。我是用异步来处理socket.BeginAccept(....)socket.BeginReceive(...)。。我在服务器端 将 多个客户端多次发送的数据 显示到一个TextBox 中时,报了一个错误:线程间操作无效: 从不是创建控件“textBox1”的线程访问它。请大家帮我分析一下产生这个错误的原因。 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 这个原因天天有人问。在 WinForm中,每个控件创建时都会隶属于一个相应的进程,这个进程有一个ID。而你如果在另外一个进程中去访问它是,就会产生这个错误了。解决的方法大体是两个:1:设置Form.CheckForIllegalCrossThreadCalls = false;系统将不进行这项检查。2:使用Invoke调用委托。Invoke将在创建这个控件的线程中调用相应委托。 跨线程调用的代码://要使用Invoke必须要传递一个委托。所以先定义一个委托类。//这个委托类使用了三个参数,看后面实际初始化委托时的代码就知道每个参数的意义了。private delegate void DelOutput(bool isSuccess, string format, object[] args);//委托的实际执行函数//在某个RichTextBox上输出一行文本,并且修改它输出的颜色。这里是直接对控件RichTextBox进行操作。//具体的代码无需关注了private void directOutput(bool isSuccess, string format, object[] args){ string o = string.Format(format, args); //o = "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "】" + o + "\n"; o += "\n"; rtbOutput.AppendText(o); rtbOutput.Select(rtbOutput.Text.Length - o.Length, o.Length); if (isSuccess) rtbOutput.SelectionColor = Color.Green; else rtbOutput.SelectionColor = Color.Red;}//任何时候你想要输出数据时调用这个函数,而不是调用上面那个。//这个函数可以根据你的调用是否是跨线程的而进行相应的处理。public void Output(bool isSuccess, string format, params object[] args){ if (this.InvokeRequired) //根据这个标志判断是否需要跨线程 { //是跨线程调用,则用Invoke的方式,在创建的线程内执行。 DelOutput indirectOutput = new DelOutput(directOutput); //注意这里是如何初始化参数数组并传入Invoke函数的。 object[] ps = new object[] { isSuccess,format,args}; this.Invoke(indirectOutput, ps); return; } else { //如果不是跨线程调用,则直接访问。 directOutput(isSuccess, format, args); }} 上面的是使用Invoke访问,在相应控件的创建线程内调用相应函数,从而使得函数实际上不在本线程中进行。另外呢,你还可以使用BackgroundWorker之类的类进行工作,当相应的工作完成时通过事件通知主线程(创建控件的线程),在事件响应代码中也是可以直接访问控件的,属于同一线程。 对于你的情况我可以想像一下大体的操作是这样的:private delegate void ChangeText(string t);//这是附加一段文本的public void AppendText(string t){ if(textBox1.InvokeRequired) { ChangeText ct = new ChangeText(AppendText); this.Invoke(ct,new object[]{t}); return; } textBox1.Text += t;}//这是完全重设文本的public void SetText(string t){ if(textBox1.InvokeRequired) { ChangeText ct = new ChangeText(SetText); this.Invoke(ct,new object[]{t}); return; } textBox1.Text = t;} phy是对的 这个问题我前天问过的 我使用socket.BeginReceive(..,new IAsynResult(myHandle),..) 在myHandle线程中处理接收到的消息。void myHandle(IAsynResult ia){ //我在这里使用 TextBox1.Text = 接收到的消息 ????这里怎么就跨线程访问了呢?} 你调用一下InvokeRequired试试看!看返回值是 true还是false。另外你可以断点停下来以后看看这个线程是否和你创建TextBox的线程ID相同。在调试工具里可以看打开的线程ID这些。 PHY 是對的,這個應該是線程安全範疇,你去baidu搜下線程安全,應該很多。 经过调试你应该看到了吧?你理解的同一个线程的想法是错误的,AsyncCallback并不是你理解的那样,在主线程中执行的。而是在工作线程,也就是BeginReceive的尾部执行的。大体上来说,BeginReceive类似于这样的:1、创建工作线程,把AsyncCallback委托以参数或者其它形式传递给线程;2、线程在结束处自动调用这个委托。BeginReceive本身只是实现了创建线程的工作。 to:phy在.NET 中提供多线程处理,同步,异步操作的接口,类,方法,Attribute,都有哪些?我在msdn中找了一下,很迷茫 这个很多,但你不需要一个个去记的,呵呵。用的时候查查MSDN,结合一般的原则就差不多了。比如你这个BeginReceive,作为一个异步的(BeginXXX应该都是异步的)的函数,它最有可能的实现方式就是线程。不然,它如何与你的主线程并行呢? msdn里面不就有说明关于线程中使用控件的安全调用和非安全调用,很详细的 WPF 和 WinForm 一样,采用 STA。MFC 也如此。其他线程访问UI需要通过事件委派的方法。比如 MFC 通过消息队列,WPF 通过 dispatcher。http://sunshaking.blogspot.com/2008/08/multi-threading-syncing-between-threads.html 在网页保存一个文件到客户端,怎样再将该文件上传到web服务器上? sql怎么在里面去除空格 超郁闷,哭求高手指点 请回答一个问题,预祝你升☆☆☆ 实现统计功能 请问vs2003中上传文件的按钮默认是Browse,怎样改成"浏览"? 问一个小问题?谢谢! c#写的DLL,调试时没问题,直接启动应用程序调用这个DLL就有问题了 怎样使程序最小化时图标不放在任务栏而是加在系统托盘中? 如何用二维码传输数据(物理隔离不联网) 请高手帮忙:C#如何设定当鼠标移到lable控件上时出现下划线? C#调用DLL指针,以及DLL载入卸载问题
在 WinForm中,每个控件创建时都会隶属于一个相应的进程,这个进程有一个ID。
而你如果在另外一个进程中去访问它是,就会产生这个错误了。
解决的方法大体是两个:
1:设置
Form.CheckForIllegalCrossThreadCalls = false;
系统将不进行这项检查。2:使用Invoke调用委托。
Invoke将在创建这个控件的线程中调用相应委托。
//这个委托类使用了三个参数,看后面实际初始化委托时的代码就知道每个参数的意义了。
private delegate void DelOutput(bool isSuccess, string format, object[] args);//委托的实际执行函数
//在某个RichTextBox上输出一行文本,并且修改它输出的颜色。这里是直接对控件RichTextBox进行操作。
//具体的代码无需关注了
private void directOutput(bool isSuccess, string format, object[] args)
{
string o = string.Format(format, args);
//o = "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "】" + o + "\n";
o += "\n";
rtbOutput.AppendText(o); rtbOutput.Select(rtbOutput.Text.Length - o.Length, o.Length);
if (isSuccess)
rtbOutput.SelectionColor = Color.Green;
else
rtbOutput.SelectionColor = Color.Red;
}//任何时候你想要输出数据时调用这个函数,而不是调用上面那个。
//这个函数可以根据你的调用是否是跨线程的而进行相应的处理。
public void Output(bool isSuccess, string format, params object[] args)
{
if (this.InvokeRequired) //根据这个标志判断是否需要跨线程
{
//是跨线程调用,则用Invoke的方式,在创建的线程内执行。
DelOutput indirectOutput = new DelOutput(directOutput);
//注意这里是如何初始化参数数组并传入Invoke函数的。
object[] ps = new object[] {
isSuccess,format,args}; this.Invoke(indirectOutput, ps);
return;
}
else
{
//如果不是跨线程调用,则直接访问。
directOutput(isSuccess, format, args);
}
}
//这是附加一段文本的
public void AppendText(string t)
{
if(textBox1.InvokeRequired)
{
ChangeText ct = new ChangeText(AppendText);
this.Invoke(ct,new object[]{t});
return;
}
textBox1.Text += t;
}
//这是完全重设文本的
public void SetText(string t)
{
if(textBox1.InvokeRequired)
{
ChangeText ct = new ChangeText(SetText);
this.Invoke(ct,new object[]{t});
return;
}
textBox1.Text = t;
}
我使用
socket.BeginReceive(..,new IAsynResult(myHandle),..)
在myHandle线程中处理接收到的消息。void myHandle(IAsynResult ia)
{
//我在这里使用 TextBox1.Text = 接收到的消息
????这里怎么就跨线程访问了呢?
}
在调试工具里可以看打开的线程ID这些。
你理解的同一个线程的想法是错误的,AsyncCallback并不是你理解的那样,在主线程中执行的。而是在工作线程,也就是BeginReceive的尾部执行的。大体上来说,BeginReceive类似于这样的:
1、创建工作线程,把AsyncCallback委托以参数或者其它形式传递给线程;
2、线程在结束处自动调用这个委托。
BeginReceive本身只是实现了创建线程的工作。
在.NET 中提供多线程处理,同步,异步操作的接口,类,方法,Attribute,都有哪些?
我在msdn中找了一下,很迷茫
不然,它如何与你的主线程并行呢?
http://sunshaking.blogspot.com/2008/08/multi-threading-syncing-between-threads.html