网站备案信息下载2024最火的十大新闻有哪些
1. 跨线程访问出现错误例子:
点击“测试”,创建一个线程,从0循环到10000给文本框赋值,代码如下:
private void Button1_Click(object sender, EventArgs e)
{//创建一个线程去执行这个方法:创建的线程默认是前台线程Thread thread = new Thread(new ThreadStart(Test));//将线程设置为后台线程thread.IsBackground = true;//Start方法标记这个线程就绪了,可以随时被执行,具体什么时候行这个线程,由CPU决定thread.Start();
}
private void Test()
{for (int i = 0; i< 10000; i++){ this.textBox1.Text = i.ToString();}
}
运行结果:
产生错误的原因:textBox1是由主线程创建的,thread线程是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。
2. 解决方案: 使用回调函数
什么叫回调函数呢?比如,你调用了一个函数,那么就叫调用,但是如果你在调用一个函数的时候,还需要把一个函数提供该函数,让这个函数来调用你的函数,那么你提供的这个函数就被称为回调函数(callback)C#的方法回调函数(机制),也是建立在委托基础上的。
使用方法回调,实现给文本框赋值:
private delegate void setTextValueCallBack(int value);
//声明回调
private setTextValueCallBack setCallBack;
private void Button1_Click(object sender, EventArgs e)
{//实例化回调setCallBack = SetValue;//创建一个线程去执行这个方法:创建的线程默认是前台线程Thread thread = new Thread(Test);//Start方法标记这个线程就绪了,可以随时被执行,具体什么时执这个线程,由CPU决定//将线程设置为后台线程thread.IsBackground = true;thread.Start();
}private void Test()
{for (int i = 0; i < 10000; i++){//textBox1.Invoke拥有此控件的基础窗口句柄的线程上执行指定的委托。//使用回调。主线程执行setCallBack委托//也就是说,Invoke()是一个方法,这方法执行的是委托,并且是在一个固定的线程上执行的。//什么样的 线程? 拥有此控件的基础窗口句柄的线程。// 这个“拥有此控件的基础窗口句柄的线程”实际上就是 “主线程”,//窗体加载时 程序会创建一个 “主线程”。if (textBox1.InvokeRequired)//如果为跨线程访问{textBox1.Invoke(setCallBack, i);//i就是传给回调函数的值}}
}
补充:Control.InvokeRequired
这个属性来进行判断是否是调用方对该控件进行调用控件,如果不是创建这个控件的线程来调用它,则返回true(即是跨线程访问就为true),否则返回False。
以上代码可以简化,只需要下面2个方法即可,利用内置委托Action,传入一个参数,内置委托即是回调函数,i是传给委托/回调函数的参数:
private void Test()
{for (int i = 0; i < 10000; i++){if (textBox1.InvokeRequired){textBox1.Invoke(new Action<int>(n => { this.textBox1.Text = n.ToString(); }),i);}}
}private void Button1_Click(object sender, EventArgs e)
{ Thread th = new Thread(Test);th.IsBackground = true;th.Start();
}
从以上回调实现的一般过程可知:C#的回调机制,实质上是委托的一种应用。
本文根据徐照兴教授讲义编写,有些改动。