多线程——一心一意,一心一意是个人觉得学习了多线程之后,感觉计算机的内部部分实现原理:多线程工作非常专一,分工明确,不能乱来,一心一意只做一件事,但是也很人性化,如果用户想要计算机在同一时间多做几件事,自己就可以再添加,本身计算机还是专一的,只是让CPU在几件事情之间来回工作。 启动任务管理器,点击进程选项,可以看到计算机正在运行的进程,一般一个应用程序对应着一个进程。
进程,一般包括一个线程,也可以有多个进程。以主线程——UI线程为主,加上添加的多线程。
计算机执行应用程序原理:CPU不是直接执行某个应用程序——进程,计算机是将一个进程,转化成一个或几个线程,然后再让CPU直接接触线程运行代码,线程起着“中间桥梁”的作用。
如果让CPU执行一个进程,实际上执行线程,而这个进程又只有一个线程,那么CPU就太浪费了,因此就有了多线程,让CPU发挥其应有的价值,运行尽可能多的线程,让计算机处理速度更快。
分析单线程出现的问题——如果给一个按钮添加事件,那么用户就无法再拖动窗体,前提是单线程的:
private void btnSingleThread_Click(object sender, EventArgs e)
{
for (int i = 0; i < 999999999; i++)
{
}
}
这是因为主线程——UI线程一心一意地在进行循环,无法再让它干其它事,所以无法拖动窗体。
多线程的出现能够很好地解决这个问题——让用户自己添加的线程进行循环运算,让主线程——UI线程操作其它事:
编写多线程并执行步骤:
S1:编写产生线程所要的方法
S2:引用命名空间:System Threading;
S3:实例化Thread类,并传入一个要运行的委托。
S4:调用Thread实例的Start()方法。
//循环计数的方法
void CountTime()
{
for (int i = 0; i < 999999999; i++)
{
}
}
//使用多线程来解决UI线程卡死问题
private void btnMulTread_Click(object sender, EventArgs e)
{
//创建线程对象,传入要线程执行的方法。
Thread threadFirst = new Thread(CountTime);
//等同于
//创建一个委托。上面方法编译器会自动完成委托的创建。
//ThreadStart ts = new ThreadStart(CountTime);
//Thread threadFirst = new Thread(ts);
//启动线程,执行方法。
threadFirst.Start();
//这个线程由主线程开启的。
}
当程序循环计算的时候,如果此时关闭主程序,实际上后台的另外一个线程仍在运行。这时候需要将这个线程改为后台线程:
前台线程:只有所有的前台线程都关闭,整个程序才会关闭,如果不设置,默认就是前台线程。
后台线程:只要所有的前台线程结束,后台线程自动结束。
//将线程设为后台线程:(当所有的前台线程结束后,后台线程会自动退出。)
threadFirst.IsBackground = true;
多线程——方法重入问题:
//修改文本框内容
void ChangeTxt()
{
for (int i = 0; i < 1000; i++)
{
int a = int.Parse(txtFirst.Text);
Console.WriteLine(Thread.CurrentThread.Name + ",a=" + a + ",i=" + i.ToString());
a++;
txtFirst.Text = a.ToString();
}
}
//方法重入问题
private void btnProblem_Click(object sender, EventArgs e)
{
//ChangeTxt()方法由UI线程创建,自己访问可以通过。
//ChangeTxt()方法此时由非UI线程访问,不被允许访问。
//在构造方法中Form1()中,加上下面这行代码,将检验关闭。
//TextBox.CheckForIllegalCrossThreadCalls = false;
Thread thread = new Thread(ChangeTxt);
thread.Name = "t1";
thread.IsBackground = true;
thread.Start();
Thread thread2 = new Thread(ChangeTxt);
thread2.Name = "t2";
thread2.IsBackground = true;
thread2.Start();
}
上面点击按钮,触发事件,当两个线程同时执行的时候,CPU就会在两个线程之间来回切换,来不及执行后面的自加运算,就切换执行另外一个线程,最终文本框的值肯定不是1000+10000,必定小于2000。
但是观察输入的i值,当第一个线程结束的时候,对应的i为999,当第二个线程结束的时候,i同样也是999。说明点击按钮事件,主线程会分别给两个线程一个方法,两个方法单独执行,互不影响。
多线程——执行带参数的方法:
void ShowTxtName(object name)
{
if (name != null)
{
MessageBox.Show("name=" + name.ToString());
}
else
{
MessageBox.Show("null");
}
}
//线程执行带参数的方法
private void btnThreadWithPara_Click(object sender, EventArgs e)
{
//ParameterizedThreadStart pts = new ParameterizedThreadStart(ShowTxtName);
//Thread thread = new Thread(ShowTxtName);
//thread.IsBackground = true;
//thread.Start();
Thread thread = new Thread(ShowTxtName);
thread.IsBackground = true;
thread.Start(txtName.Text);
}
多线程——执行带多个参数的方法:
void ShowTxtName2(object li)
{
List<string> list = li as List<string>;
if (list != null)
{
foreach (string s in list)
{
MessageBox.Show(s);
}
}
}
//执行带多个参数的方法
private void btnThreadWithManyPara_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ShowTxtName2);
thread.IsBackground = true;
//执行方法,将参数传过来。
thread.Start(new List<string>() { "First", "Second", "Third" });
}
备注:写于2013年7月31日