欢迎光临散文网 会员登陆 & 注册

C#多线程2

2020-09-23 09:56 作者:微软MVP-Eleven  | 我要投稿


2.2 线程的常用属性

2.2.1 线程的标识符 ManagedThreadId是确认线程的唯一标识符,程序在大部分情况下都是通过Thread.ManagedThreadId来辨别线程的。而Name是一个可变值,在默认时候,Name为一个空值 Null,开发人员可以通过程序设置线程的名称,但这只是一个辅助功能。 2.2.2 线程的优先级别 当线程之间争夺CPU时间时,CPU按照线程的优先级给予服务。高优先级的线程可以完全阻止低优先级的线程执行。.NET为线程设置了Priority属性来定义线程执行的优先级别,里面包含5个选项,其中Normal是默认值。除非系统有特殊要求,否则不应该随便设置线程的优先级别。

2.2.3 线程的状态 通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。 前面说过,一个应用程序域中可能包括多个上下文,而通过CurrentContext可以获取线程当前的上下文。 CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。 2.2.4 System.Threading.Thread的方法 Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用。

线城示例:

运行结果:

2.3 前台线程和后台线程 前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程              都是前台线程 后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。 通过BeginXXX方法运行的线程都是后台线程。

运行结果:前台线程执行完,后台线程未执行完,程序自动结束。

把bThread.IsBackground = true注释掉,运行结果:主线程执行完毕后(Main函数),程序并未结束,而是要等所有的前台线程结束以后才会结束。

后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。 2.4 线程同步 所谓同步:是指在某一时刻只有一个线程可以访问变量。 如果不能确保对变量的访问是同步的,就会产生错误。 c#为同步访问变量提供了一个非常简单的方式,即使用c#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在c#中,关键字Lock定义如下: Lock(expression) {   statement_block } expression代表你希望跟踪的对象:           如果你想保护一个类的实例,一般地,你可以使用this;           如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了 而statement_block就算互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。 以书店卖书为例

运行结果:

从运行结果可以看出,两个线程同步访问共享资源,没有考虑同步的问题,结果不正确。 考虑线程同步,改进后的代码:

运行结果:

2.5 跨线程访问

点击“测试”,创建一个线程,从0循环到10000给文本框赋值,代码如下:

运行结果:

产生错误的原因:textBox1是由主线程创建的,thread线程是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。 解决方案: 1、在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。 private void Form1_Load(object sender, EventArgs e) {        //取消跨线程的访问        Control.CheckForIllegalCrossThreadCalls = false; } 使用上述的方法虽然可以保证程序正常运行并实现应用的功能,但是在实际的软件开发中,做如此设置是不安全的(不符合.NET的安全规范),在产品软件的开发中,此类情况是不允许的。如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。 2、使用回调函数 回调实现的一般过程: C#的方法回调机制,也是建立在委托基础上的,下面给出它的典型实现过程。 (1)、定义、声明回调。 1 //定义回调 2 private delegate void DoSomeCallBack(Type para); 3 //声明回调 4 DoSomeCallBack doSomaCallBack; 可以看出,这里定义声明的“回调”(doSomaCallBack)其实就是一个委托。 (2)、初始化回调方法。 doSomeCallBack=new DoSomeCallBack(DoSomeMethod); 所谓“初始化回调方法”实际上就是实例化刚刚定义了的委托,这里作为参数的DoSomeMethod称为“回调方法”,它封装了对另一个线程中目标对象(窗体控件或其他类)的操作代码。 (3)、触发对象动作 Opt  obj.Invoke(doSomeCallBack,arg); 其中Opt obj为目标操作对象,在此假设它是某控件,故调用其Invoke方法。Invoke方法签名为: object  Control.Invoke(Delegate  method,params  object[] args); 它的第一个参数为委托类型,可见“触发对象动作”的本质,就是把委托doSomeCallBack作为参数传递给控件的Invoke方法,这与委托的使用方式是一模一样的。 最终作用于对象Opt obj的代码是置于回调方法体DoSomeMethod()中的,如下所示: private void DoSomeMethod(type para) {     //方法体    Opt obj.someMethod(para); } 如果不用回调,而是直接在程序中使用“Opt obj.someMethod(para);”,则当对象Opt obj不在本线程(跨线程访问)时就会发生上面所示的错误。 从以上回调实现的一般过程可知:C#的回调机制,实质上是委托的一种应用。在C#网络编程中,回调的应用是非常普遍的,有了方法回调,就可以在.NET上写出线程安全的代码了。 使用方法回调,实现给文本框赋值:

本文转载自博客园:https://www.cnblogs.com/dotnet261010/p/6159984.html


C#多线程2的评论 (共 条)

分享到微博请遵守国家法律