C#---多线程
线程介绍:前台线程和后台线程。
- 前台线程:只有所有的前台线程都关闭才能完成程序关闭。
- 后台线程:只要所有的前台线程结束,后台线程自动结束。
1、创建一个WINFORM窗体程序,在窗体上添加一个按钮控件和TextBox控件。并为按钮添加一个事件。
修改Form1.cs的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace 多线程
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Test(); //调用自定义Test()函数
}
private void Test() //在控制台打印输出10000个数
{
for (int i = 0; i < 10000;i++ )
{
Console.WriteLine(i);
}
}
}
}
运行结果:可以通过“调试菜单---->窗口---->输出”查看输出结果。
注意:但有一个问题单击按钮后,数字未打印完10000个数字,FORM窗体不能移动。并且未打印完成不能关闭窗体。
(1)单线程问题:点击按钮后,主线程去运行Test()函数进行打印输出。本来主线程是用于窗体的运行。
2、使用多线程方法一
修改Form1.cs的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading; //添加线程命名空间(首先选择,快捷键:alt+shift+F10)
namespace 多线程
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//创建一个线程去执行这个方法
Thread th = new Thread(Test); //用线程调用Test()方法
th.Start();//标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程由CPU决定。
th.IsBackground = true; //将线程设置为后台线程
}
private void Test()
{
for (int i = 0; i < 10000;i++ )
{
Console.WriteLine(i);
}
}
}
}
运行结果:
未设置为后台线程前:单击按钮后,数字未打印完10000个数字,FORM窗体可以进行移动。并且未打印完成可以关闭窗体。但程序依旧在运行。
设置为后台线程后:单击按钮后,数字未打印完10000个数字,FORM窗体可以进行移动。并且未打印完成可以关闭窗体。关闭窗体的同时程序退出运行。
3、使用多线程方法二
(1)把打印的值传给TextBox控件显示
修改Form1.cs的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading; //添加线程命名空间(首先选择快捷键:alt+shift+F10)
namespace 多线程
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//创建一个线程去执行这个方法
Thread th = new Thread(Test); //用线程调用Test()方法
th.Start();//标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程由CPU决定。
th.IsBackground = true; //将线程设置为后台线程
}
private void Test()
{
for (int i = 0; i < 10000;i++ )
{
textBox1.Text = i.ToString(); //把打印的值传给TextBox控件显示
}
}
}
}
运行结果:
运行会抛出异常。
主线程创建了窗体,新创建的线程调用Test()方法,方法中用到了textBox控件,即新建线程要调用主线程的资源报错。
(2)为FORM添加Load事件,并修改Form1.cs的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading; //添加线程命名空间(首先选择快捷键:alt+shift+F10)
namespace 多线程
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//创建一个线程去执行这个方法
Thread th = new Thread(Test); //用线程调用Test()方法
th.Start();//标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程由CPU决定。
th.IsBackground = true;//将线程设置为后台线程
}
private void Test()
{
for (int i = 0; i < 10000;i++ )
{
textBox1.Text = i.ToString(); //把打印的值传给TextBox控件显示
}
}
private void Form1_Load(object sender, EventArgs e)
{
//取消跨线程的访问----
Control.CheckForIllegalCrossThreadCalls = false; //不捕获对错误线程的调用
}
}
}
运行结果:
但是在关闭程序时,偶尔也会抛出异常。
主要原因:关闭程序时,新线程没有马上关闭,新建线程仍然在访问文本框控件;但是主线程已经关闭,文本框已经被释放掉不存在了,所以抛出异常。
解决方法:
为窗体关闭添加“FormClosing”事件,并修改Form1.cs的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading; //添加线程命名空间(首先选择快捷键:alt+shift+F10)
namespace 多线程
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Thread th; //为了Form1_FormClosing()访问到th
private void button1_Click(object sender, EventArgs e)
{
//创建一个线程去执行这个方法
th = new Thread(Test); //用线程调用Test()方法
th.Start();//标记这个线程准备就绪了,可以随时被执行。具体什么时候执行这个线程由CPU决定。
th.IsBackground = true;//将线程设置为后台线程
}
private void Test()
{
for (int i = 0; i < 10000;i++ )
{
textBox1.Text = i.ToString(); //把打印的值传给TextBox控件显示
}
}
private void Form1_Load(object sender, EventArgs e)
{
//取消跨线程的访问----
Control.CheckForIllegalCrossThreadCalls = false; //不捕获对错误线程的调用
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//当点击关闭窗体的时候,先判断新线程是否为null(新线程已经关闭)
if (th!=null)
{
th.Abort(); //结束这个线程
}
}
}
}
关闭可以正常;关闭后新建线程没法再被重新启动,即线程被Abort()后不能被重新Start()。
4、线程静态方法使用
Thread.Sleep(1)静态方法,可以使当前线程停止一段时间运行。
(1)新建一个C#控制台程序,其代码为:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //线程命名空间
namespace 多线程静态方法
{
class Program
{
static void Main(string[] args)
{
//线程静态方法
Thread.Sleep(3000); //延时3000毫秒即3秒
Console.WriteLine("hello world");
Console.ReadLine();
}
}
}
运行结果:
延时3秒后,控制台才打印出hello world.