【黑马程序员】多线程的方法重入问题
2013-09-23 16:43
155 查看
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流!
----------------------
由于多线程是让CPU在“同一时间”响应用户不同的操作,因此在程序运行过程中,很容易产生一些让人意想不到的结果。在说方法重入问题之前,先看一个示例吧。
有一个如图所示的窗体应用程序,窗体里面有一个TextBox控件和一个Button控件,TextBox控件的初始值为0:
下面是主要方法的代码 :
运行这段程序,其结果如图所示:
程序运如图所示的位置就会报错,这是什么原因呢?从报错的信息中,我们可以大概了解到,txtNum这个控件是由主线程创建的,但是我们是在另外一个新建的线程中调用它,这是不允许的,所以线程之间的操作无效。在主线程里面创建的控件只能在主线程里面使用,不能使用其它线程。但这不是绝对的,我们可以在初始化窗体的时候使用如下图所示的一段代码把微软的检查关掉。
我们再运行程序,点击按钮,这时就不会报错了。运行结果如下图所示,由于循环了10000次,因此txtNum的值最后是10000。
下面,我们再把这段程序做一个改动,在代码中增加一个线程,这两个线程都调用同一个方法,代码如下所示:
再次运行程序,点击按钮,我们会得到什么结果呢?按照我们一贯的思维理解,既然ChangeText()方法被调用了两遍,那么两个线程都执行完毕之后,txtNum这个控件的值应该是20000。真的是这个值吗?下图是这段程序的运行结果:
程序的实际结果大大出乎我们的预料,才10004,没到20000。这是怎么回事呢?这实际就是线程重入问题的表象。我们可以在ChangeText()方法的for循环里面加上一段代码来调试查看a值的变化情况。并且我们还可以给每个线程取一个名字,然后在输出窗口里面查看CPU当前调用了哪个线程。代码如下:
再次运行程序,在点击按钮之前先打开输出窗口,以便在程序运行时观察值的变化。结果如图所示:
从图中,我们可以看到,当线程T2取出txtNum里面的值之后,并没有马上进行运算,而是把CPU的使用权交给线程T1,让T1再次去取txtNum控件的值,由于此时txtNum的值没有改变,因此T1取出的值与T2一样。也就是说,txtNum的值大部分都要被取出两遍,但不是所有的值都会被取两遍。这是因为所有线程的开始和结束时间都有先后之分,当T2线程结束之后,就只有T1线程去取txtNum的值。由最后i的值可以看出,每个线程都执行了1000遍循环。当两个线程同时去获取一个相同控件的值的时候,会轮流获取。
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流!
----------------------
----------------------
由于多线程是让CPU在“同一时间”响应用户不同的操作,因此在程序运行过程中,很容易产生一些让人意想不到的结果。在说方法重入问题之前,先看一个示例吧。
有一个如图所示的窗体应用程序,窗体里面有一个TextBox控件和一个Button控件,TextBox控件的初始值为0:
下面是主要方法的代码 :
/// <summary> /// 多线程方法重入问题按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMethodProblem_Click(object sender, EventArgs e) { Thread thread = new Thread(ChangeText); //这里可以直接把方法传入 thread.IsBackground = true; //把线程设置为后台线程 thread.Start(); } /// <summary> /// 修改文本框里面的内容(循环取得文本框的值,然后让它自增,最后再把它赋值给文本框) /// </summary> private void ChangeText() { for (int i = 0; i < 10000; i++) { int a = int.Parse(txtNum.Text); a++; txtNum.Text = a.ToString(); } }
运行这段程序,其结果如图所示:
程序运如图所示的位置就会报错,这是什么原因呢?从报错的信息中,我们可以大概了解到,txtNum这个控件是由主线程创建的,但是我们是在另外一个新建的线程中调用它,这是不允许的,所以线程之间的操作无效。在主线程里面创建的控件只能在主线程里面使用,不能使用其它线程。但这不是绝对的,我们可以在初始化窗体的时候使用如下图所示的一段代码把微软的检查关掉。
我们再运行程序,点击按钮,这时就不会报错了。运行结果如下图所示,由于循环了10000次,因此txtNum的值最后是10000。
下面,我们再把这段程序做一个改动,在代码中增加一个线程,这两个线程都调用同一个方法,代码如下所示:
/// <summary> /// 多线程方法重入问题按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMethodProblem_Click(object sender, EventArgs e) { Thread thread = new Thread(ChangeText); //这里可以直接把方法传入 thread.IsBackground = true; //把线程设置为后台线程 thread.Start(); //增加一个线程 Thread thread2 = new Thread(ChangeText); //这里可以直接把方法传入 thread2.IsBackground = true; //把线程设置为后台线程 thread2.Start(); } /// <summary> /// 修改文本框里面的内容(循环取得文本框的值,然后让它自增,最后再把它赋值给文本框) /// </summary> private void ChangeText() { for (int i = 0; i < 10000; i++) { int a = int.Parse(txtNum.Text); a++; txtNum.Text = a.ToString(); } }
再次运行程序,点击按钮,我们会得到什么结果呢?按照我们一贯的思维理解,既然ChangeText()方法被调用了两遍,那么两个线程都执行完毕之后,txtNum这个控件的值应该是20000。真的是这个值吗?下图是这段程序的运行结果:
程序的实际结果大大出乎我们的预料,才10004,没到20000。这是怎么回事呢?这实际就是线程重入问题的表象。我们可以在ChangeText()方法的for循环里面加上一段代码来调试查看a值的变化情况。并且我们还可以给每个线程取一个名字,然后在输出窗口里面查看CPU当前调用了哪个线程。代码如下:
/// <summary> /// 多线程方法重入问题按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMethodProblem_Click(object sender, EventArgs e) { Thread thread = new Thread(ChangeText); //这里可以直接把方法传入 thread.Name = "T1"; //给线程取名为“T1” thread.IsBackground = true; //把线程设置为后台线程 thread.Start(); //增加一个线程 Thread thread2 = new Thread(ChangeText); //这里可以直接把方法传入 thread2.Name = "T2"; //给线程取名为“T2” thread2.IsBackground = true; //把线程设置为后台线程 thread2.Start(); } /// <summary> /// 修改文本框里面的内容(循环取得文本框的值,然后让它自增,最后再把它赋值给文本框) /// </summary> private void ChangeText() { for (int i = 0; i < 1000; i++) //把循环次数改小一点,不然程序运行时间太长 { int a = int.Parse(txtNum.Text); //在这里利用Thread类的CurrentThread属性获取当前正在运行的线程,然后在输出窗口中 //打印出它的名字,并且打印出当前获取到的a的值和程序的运行次数,也就是i的值 Console.WriteLine(Thread.CurrentThread.Name + ",a=" + a + ",i=" + i.ToString()); a++; txtNum.Text = a.ToString(); } }
再次运行程序,在点击按钮之前先打开输出窗口,以便在程序运行时观察值的变化。结果如图所示:
从图中,我们可以看到,当线程T2取出txtNum里面的值之后,并没有马上进行运算,而是把CPU的使用权交给线程T1,让T1再次去取txtNum控件的值,由于此时txtNum的值没有改变,因此T1取出的值与T2一样。也就是说,txtNum的值大部分都要被取出两遍,但不是所有的值都会被取两遍。这是因为所有线程的开始和结束时间都有先后之分,当T2线程结束之后,就只有T1线程去取txtNum的值。由最后i的值可以看出,每个线程都执行了1000遍循环。当两个线程同时去获取一个相同控件的值的时候,会轮流获取。
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流!
----------------------
相关文章推荐
- 多线程-同步代码快的锁及同步方法应用和锁的问题
- 多线程实现生产者消费者问题 详细注释 事件+临界区 信号量+临界区2种方法
- java基础问题---多线程有几种实现方法
- 黑马程序员 Java学习总结之多线程中start方法和run方法的区别(这个一定要搞清楚)
- Linux下运行多线程——undefined reference to 'pthread_create'问题两种解决方法
- 多线程下双重检查锁的问题及解决方法
- 在多线程中使用静态方法是否有线程安全问题
- day11多线程,run方法。卖票多线程示例。synchronized,同步锁对象,回顾单例。死锁问题
- Java多线程对耗时方法的同步问题
- 在多线程中使用静态方法是否有线程安全问题
- 多线程下生产者消费者问题的五种同步方法实现
- 黑马程序员---java多线程的一些常见问题
- 基于消息分发的多线程程序设计,常见的问题,以及解决方法
- 多线程解决安全问题的方法(Synchronized)
- 黑马程序员—多线程(下)--多线程安全问题及唤醒等待机制
- 黑马程序员——java多线程的几种实现方法及多窗口售票小程序
- 黑马程序员_实现多线程的2种方法
- 多线程访问文件问题中WaitForSingleObject方法的使用,及其效率!
- 黑马程序员-解决系统报错“线程间操作无效: 从不是创建控件" txtbox1" 的线程访问它”问题的方法
- 黑马程序员------多线程(No.1)(概述、线程的创建、安全问题、同步锁、同步函数)