1.为什么要使用多线程,单线程的缺点是什么?
下面我们用一个窗体程序来进行解释,首先我们创建一个窗体,加入一个按钮和文本框
进入按钮单击事件
private void button1_Click(object sender, EventArgs e)
{
Test();
}
然后写一个Tesr方法来测试
public void Test()
{
for (int i = 0; i <100000; i++)
{
Console.WriteLine(i);
}
}
当我们运行,单击按钮时,执行Test()方法,可以在输出窗口中看到输出I的值,当在输出i的值时是无法对窗体进行操作的,只有当test()执行完以后才能对窗体进行操作!
如果窗体能移动,可以将i的值设置大点,然后再运行(设置太大可能会出现等很久的情况。)
注:输出窗口可以在视图菜单中找到
出现这种情况的原因是当运行窗体时,主线程在初始化窗体,当点击按钮,主线程转去执行Test()方法,所以窗体无法移动,只有当主线程输出完以后才能对窗体进行操作。
那么如何去解决这歌问题了,这时就引入了多线程的概念,我们可以创建一个线程去执行Test()方法,是主线程运行窗体,我们使用Thread类来创建新的线程,但是首先要引入Threadl类的命名空间——using System.Threading;
我们使用下面的方法让线程执行Test方法
官方解释:
//
// 摘要:
// 初始化 System.Threading.Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托。
//
// 参数:
// start:
// 一个委托,它表示此线程开始执行时要调用的方法。
//
// 异常:
// T:System.ArgumentNullException:
// start 为 null。
[SecuritySafeCritical]
public Thread(ParameterizedThreadStart start);
private void button1_Click(object sender, EventArgs e)
{
//创建一个线程去执行这个方法
Thread th = new Thread(Test);
//标记这个线程准备就绪了,可以随时被执行,具体什么时候执行,由cpu决定
th.Start();
}
我们用上述方法创建一个新线程来执行Test,这样刚刚单线程带来的困扰就能够成功的解决了
但是出现了一个新问题,当输出窗口还在输出i的值得时候,我们单击窗体关闭按钮,发现输出窗口依然还在输出i的值,这是为什么呢
这是出现了两个概念:前台线程和后台线程
前台线程:只有所有的前台线程都关闭才能完成程序关闭。
后台线程:只要所有的前台线程结束,后台线程自动结束
默认创建的线程是前台线程
所以我们需要将程序设置成为后台线程
//利用这个属性来设置线程为后台线程
th.IsBackground = true;
现在单击窗体关闭按钮,输出窗口也停止了!
2。多线程的一些方法和属性
现在我们将ii值输出到文本框里会出现什么呢?
出现了如上的错误:线程间操作无效: 从不是创建控件“textBox1”的线程访问它。
这是因为新线程要访问主线程创建的资源,跨线程访问所出现的错误
解决的办法:当我们窗体加载的时候,设置
private void Form1_Load(object sender, EventArgs e)
{
//取消跨线程的访问
Control.CheckForIllegalCrossThreadCalls = false;
}
这样我们就可以在文本框上成功显示i的值了
但是当你关闭窗体按钮时,有可能会出现下面这种情况,这是因为线程由于延迟不会立即关闭,但是当你打点击关闭按钮时,主线程已经关闭了,但是新创建的线程因为延迟没有立即关闭,会继续去访问文本框,所以报出下面的异常
解决的办法是当点击关闭按钮的时候,就去判断线程是否为null,如果不是,则调用Abort方法来停止线程
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//th要写在外面,这样才能够访问到
if(th!=null)
{
th.Abort();
}
}
这样就不会出现这个异常了。
但是终止以后的线程不能再重新开始,否则会出现下面这种情况
线程的一些方法:
Sleep()方法:让当前线程暂停一段时间再执行
Name:线程名
CurrentThread:当前线程的引用
上面的例子只是简单的理解了下线程,线程还有很多东西没有讲到,后面学习了会继续补充!