C# 线程的基本操作(完)

07  线程同步

在应用程序中使用多个线程的一个好处是每个线程都可以异步执行。对于 Windoms 应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应。对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力;否则,在完全满足前一个请求之前,将无法处理每个新请求,然而,线程的异步性意味着必须协调对资源(如文件句柄、网络连接和内存》的访问:否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作,结果将产生不可预知的数据损坏。

线程同步是指并发线程高效、有序地访问共享资源所采用的技术,所谓同步,是指某一时刻只有一个线程可以访问资源,只有当资源所有者主动放弃了代码或资源的所有权时,其他线程才可以使用这些资源。

线程同步可以分别使用 C#中的 lock 关键字、Monitor 类和 Mutex 类实现,下面对这几种实现方法进行详细介绍。

1.  使用 C#中的 lock 关键字实现线程同步

lock 关键字可以用来确保代码块完整运行,而不会被其他线程中断,它是通过在代码块运行期间为给定对象获取互斥锁来实现的。

lock 语句以关键字 lock 开头,它有一个作为参数的对象,在该参数的后面还有一个一次只能由一个线程执行的代码块。

lock 语句的语法格式如下: 

Object thisLock = new Object();
lock(thisLock)
{     
     //要运行的代码块
}

提供给 lock 语句的参数必须为基于引用类型的对象,该对象用来定义锁的范围。严格地说,提供给 lock 语句的参数只是用来唯一标识由多个线程共享的资源,所以它可以是任意类实例。然而,该参数通常表示需要进行线程同步的资源。例如,如果一个容器对象被多个线程使用,则可以将该容器传递给 lock 语句,而 lock 语句中的代码块将访问该容器,如果其他线程在访问该容器前先锁定该容器,则对该对象的访问将是安全同步的。

通常,最好避免锁定 public 类型或不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则lock(this)可能会有问题,因为不受控制的代码也可能会锁定该对象,这将可能导致死锁,即两个或更多个线程等待释放同一个对象,出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题,锁定字符串尤其危险。因为字符串被公共语言运行库(CLR)“暂留”,这意味着整个程序中任何给定字符串都只有一个实例。因此,如果在应用程序进程中的任何具有相同内容的字符串上放置锁,那么将锁定应用程序中该字符串的所有实例。因此,最好锁定不会被暂留的私有或受保护成员。

 说明

       事实上 lock 语句是用 Monitor 类来实现的,它等效于 try/finally 语句块,使用 lock 关键字通常比直接使用 Monitor 类更可取,一方面是因为 lock 更简洁;另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放寄出监视器。这是通过 finally 关键字来实现的、无论是否引发异常它执行关联的代码块。

【例8】 创建一个控制台应用程序,其中自定义一个 LockThread 方法,该方法中使用 lock 关键字锁定当前线程,然后在 Main 方法中通过 Program 的类对象调用 LockThread 自定义方法。

代码如下:

static void Main(string[] args)
{
    Program myProgram = new Program();       //实例化类对象 
    myProgram.LockThread();                 //调用锁定线程方法 
}
void LockThread()
{
    lock(this)                             //锁定当前线程,以实现同步
    {
        Console.WriteLine("锁定线程以实现线程同步");
    } 
}

2.  使用 Monitor 类实现线程同步

Monitor类提供了同步对象的访问机制,它通过向单个线程授予对象锁来控制对对象的访问,对象锁提供限制访问代码块(通常称为临界区)的能力。当一个线程拥有对象锁时,其他任何线程都不能获取该锁。

Monitor 类的主要功能如下:

(1)它根据需要与某个对象相关联。

(2)它是未绑定的,也就是说可以直接从任何上下文调用它。

(3)不能创建 Monitor 类的实例。

Monitor 类的常用方法及说明如表4  所示。

表4   Monitor 类的常用方法及说明

783d60d8252d3eb9ffec02ea3223a068.png

 注意

       使用 Monitor 类锁定的是对象(即引用类型)而不是值类型。

【例9】创建一个控制台应用程序,其中自定义一个LockThread 方法,该方法中首先使用 Monitor 类的 Enter 方法锁定当前的线。然后再调用 Monitor 类的 Exit 方法释放当前线程,最后在 Main 方法中通过 Program 的类对象调用 LockThread 自定义方法。

代码如下:

static void Main(string[] args)
{
     Program myPrograme = new Program();            //实例化类对象 
     myProgram.LockThread();                        //调用锁定线程方法 
}
void LockThread()
{
     Monitor.Enter(this);                          //锁定当前线程 
     Console.WriteLine("锁定线程以实现线程同步");
     Monitor.Exit(this);                           //释放当前线程

 说明

       从上述两个例子来看,这两个例子实现的功能是相同的,但似乎使用 LOCK 关键字更简单一些,那为何还要使用 Monitor 类呢?因为使用 Monitor 类有更好的控制能力。例如,它可以使用 Wait 方法指示活动的线程等待一段时间,当线程完成操作时,还可以使用 Pulse 方法或 PulseAll方法通知等待中的线程。

3.  使用 Mutex 类实现线程同步

当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。Mutex 类是同步基元,它只向一个线程授予对共享资源的独占访问。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体,Mutex 类与监视器类似,它防止多个线程在某一时间同时执行某个代码块,然而与监视器不同的是,Mutex 类可以用来使跨进程的线程同步。

可以使用 WaitHandle.WaitOne 方法请求互斥体的所属权,拥有互斥体的线程可以在对 WaitOne方法的重复调用中请求相同的互斥体而不会阻止其执行,但线程必须调用同样多次数的 ReleaseMutex 方法以释放互斥体的所属权。Mutex 类强制线程标识,因此互斥体只能由获得它的线程释放。

当用于进程间同步时,Mutex 称为“命名 Mutex”,因为它将用于另一个应用程序,因此它不能通过全局变量或静态变量共享。必须给它指定一个名称,才能使两个应用程序访问同一个 Mutex 对象。

Mutex 类的常用方法及说明如表5  所示。

表5   Mutex 类的常用方法及说明

586922051dbc44a67cf93eb2a0e0bd04.png

使用 Mutex 类实现线程同步很简单,首先实例化一个 Mutex 类对象,它的构造函数中比较常用的有 public Mutex(bool initallyOwned)。其中,参数 initallyOwned 指定了创建该对象的线程是否希望立即获得其所有权,当在一个资源得到保护的类中创建 Mutex 类对象时,常将该参数设置为 false。然后在需要单线程访问的地方调用其等待方法,等待方法请求 Mutex 对象的所有权。这时,如果该所有权被另一个线程所拥有,则阻塞请求线程,并将其放入等待队列中,请求线程将保持阻塞,直到 Mutex 对象收到了其所有者线程发出将其释放的信号为止。所有者线程在终止时释放 Mutex 对象,或者调用 ReleaseMutex 方法来释放 Mutex 对象。

 说明

       尽管 Mutex 类可以用于进程内的线程同步,但是使用Monitor 类通常更为可取,因为 Monitor 监视器是专门为.NET Framework 而设计的,因而它可以更好地利用资源。相比之下, Mutex 类是 Win32 构造的包装。尽管Mutex 类比监视器更为强大,但是相对于  Monitor 类,它所需要的互操作转换更消耗计算机资源。

【例10】创建一个控制台应用程序,其中自定义了一个 LockThread 方法,该方法中首先使用 Mutex 类对象的 WaitOne 方法阻止当前线程。然后再调用 Mutex 类对象的 ReleaseMutex 方法释放 Mutex对象,即释放当前的线程。最后在 Main 方法中通过 Program 的类对象调用 LockThread 自定义方法。

代码如下:

static void Main(string[] args)
{
     Program myProgram = new Program();                //实例化类对象 
     myProgram.LockThread();                           //调用锁定线程方法 
}
void LockThread()
{
    Mutex myMutex=new Mutex(false);                    //实例化 Mutex 类对象 
    myMutex.WaitOne();                                 //阻止当前线程 
    Console.WriteLine("锁定线程以实现线程同步");
    myMutex.ReleaseMutex();                            //释放 Mutex 对象 
}

- END -


be3b58172635a0007c96de33cfc082c6.png


关注小编不迷路呦

-----------------------------------

需要进技术群交流的,请添加小编mm1552923

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值