`
yaoyao19851023
  • 浏览: 32171 次
  • 性别: Icon_minigender_2
  • 来自: 大连
最近访客 更多访客>>
社区版块
存档分类
最新评论

C #中的几个线程同步对象的技巧

    博客分类:
  • jxl
阅读更多
[/size] 字体大小: 标准1 (xx-small)2 (x-small)3 (small)4 (medium)5 (large)6 (x-large)7 (xx-large) 对齐: 标准居左居中居右[size=x-small]C #中的几个线程同步对象的技巧
很明显Add方法和Delete方法是不能同时被调用的,所以必须进行线程同步处理。简单的方法是用lock语句:

public class Tools
{
private object abcde = new object();
private int count = 100;

public void Add(int n)
{
lock(abcde)
{
count+=n;
}
}

public void Delete(int n)
{
lock(abcde)
{
count-=n;
}
}
}

  其中abcde是一个private级的内部变量,它不表示任何的意义,只是作为一种“令牌”的角色。
 
  当执行Add方法中的lock(abcde)方法时,这个令牌就在Add方法的手中了,如果这时有第二个线程也想拿这个令牌,没门,惟有等待。一旦第一个lock语句的花括号范围结束之后,这时令牌就被释放了,同时会迅速落到第二个线程的手中,并且排除其他后来的人。

使用Monitor类的方法大致一样:

public class Tools
{
private object abcde = new object();
private int count = 100;

public void Add(int n)
{
Monitor.Enter(abcde);
count+=n;
Monitor.Exit(abcde);
}

public void Delete(int n)
{
Monitor.Enter(abcde);
count-=n;
Monitor.Exit(abcde);
}
}

Monitor的常用方法:Enter和Exit都是静态方法,作用跟lock语句的两个花括号一样。
而使用 Mutex 就不需声明一个“令牌”对象了,但要实例化之后才可以使用:

public class Tools
{
private Mutex mut = new Mutex();
private int count = 100;

public void Add(int n)
{
mut.WaitOne();
count+=n;
mut.ReleaseMutex();
}

public void Delete(int n)
{
mut.WaitOne();
count-=n;
mut.ReleaseMutex();
}
}

其中的WaitOne为等待方法,一直等到Mutex 被释放为止。初始的情况下,Mutex 对象是处于释放状态的,而一旦执行了WaitOne方法之后,它
就被捕获了,一直到被调用了ReleaseMutex方法之后才被释放。
使用这三种方法都有一个要注意的问题,就是在独占代码段里面如果引起了异常,可能会使“令牌”对象不被释放,这样程序就会一直地死等下去了。
所以要在独占代码段里面处理好异常。例如下面这样的代码就是错误的:

public void Add(int n)
{
try
{
mut.WaitOne();
count+=n;
//....这里省略了N行代码
//....这里是有可能引起异常的代码
//....这里省略了N行代码
mut.ReleaseMutex();
}
catch
{
Console.Writeline("error.");
}
}

上面的代码一旦在try和catch里面发生了异常,那么Mutex就不能被释放,后面的程序就会卡死在WaitOne()一行,而应该改成这样:

public void Add(int n)
{
mut.WaitOne();
try
{
count+=n;
//....这里省略了N行代码
//....这里是有可能引起异常的代码
//....这里省略了N行代码
}
catch
{
Console.Writeline("error.");
}
mut.ReleaseMutex();
}

现在谈一下第二种:
ManualResetEvent类,AutoResetEvent类

上面这两个类都是由EventWaitHandle类派生出来的,所以功能和调用方法都很相似。
这两个类常用于阻断某个线程的执行,然后在符合条件的情况下再恢复其执行。
举个例子,你想送花给一个MM,托了一个送花的小伙子送了过去,而你希望当MM收到花之后就立即打个电话过去告诉她。

但问题是你不知道花什么时候才送到MM的手里,打早了打迟了都不好,这时你可以使用ManualResetEvent对象帮忙。当委

托小伙子送花过去的时候,使用ManualResetEvent的WaitOne方法进行等待。当小伙子把花送到MM的手中时,再调用一下

ManualResetEvent的Set方法,你就可以准时地打电话过去了。
另外ManualResetEvent还有一个Reset方法,用来重新阻断调用者执行的,情况就好比你委托了这个小伙子送花给N个MM,

而又想准时地给这N个MM打电话的情况一样。

using System;
using System.Threading;

public class TestMain
{
private static ManualResetEvent ent = new ManualResetEvent(false);

public static void Main()
{
Boy sender = new Boy(ent);
Thread th = new Thread(new ThreadStart(sender.SendFlower));
th.Start();

ent.WaitOne(); //等待工作
Console.WriteLine("收到了吧,花是我送嘀:)");
Console.ReadLine();
}

}

public class Boy
{
ManualResetEvent ent;

public Boy(ManualResetEvent e)
{
ent = e;
}

public void SendFlower()
{
Console.WriteLine("正在送花的途中");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
Console.Write("..");
}
Console.WriteLine("\r\n花已经送到MM手中了,boss");

ent.Set(); //通知阻塞程序
}
}

而AutoResetEvent类故名思意,就是在每次Set完之后自动Reset。让执行程序重新进入阻塞状态。
即AutoResetEvent.Set() 相当于 ManualResetEvent.Set() 之后又立即 ManualResetEvent.Reset(),
其他的就没有什么不同的了。
举个送花给N个MM的例子:

using System;
using System.Threading;

public class TestMain
{
private static AutoResetEvent ent = new AutoResetEvent(false);

public static void Main()
{
Boy sender = new Boy(ent);

for (int i = 0; i < 3; i++)
{
Thread th = new Thread(new ThreadStart(sender.SendFlower));
th.Start();
ent.WaitOne(); //等待工作
Console.WriteLine("收到了吧,花是我送嘀:)\r\n\r\n");
}

Console.ReadLine();
}

}

public class Boy
{
AutoResetEvent ent;

public Boy(AutoResetEvent e)
{
ent = e;
}

public void SendFlower()
{
Console.WriteLine("正在送花的途中");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
Console.Write("..");
}
Console.WriteLine("\r\n花已经送到MM手中了,boss");

ent.Set(); //通知阻塞程序,这里的效果相当于 ManualResetEvent的Set()方法+Reset()方法
}
}

要注意的是ManualResetEvent和AutoResetEvent 的构造函数都有一个bool的参数,

分享到:
评论

相关推荐

    ios多线程指南

    多线程是指在一个程序中同时运行多个执行单元(即线程),每个线程都可以独立执行任务。在iOS开发中,多线程能够充分利用设备的多核处理器能力,提高应用响应速度并提升用户体验。 ##### 1.2 线程术语 - **线程**:...

    AS/400-多线程编程技术.pdf

    线程是进程内的最小可调度单元,同一进程中的多个线程共享进程的资源(如内存空间)。线程之间可以通过共享数据进行通信。 ###### 1.3.1 线程的分类 - 用户线程:由用户程序创建并管理的线程。 - 内核线程:由操作...

    多线程编程指南

    线程同步是多线程编程中最关键的部分之一,用于防止数据竞争和死锁等问题。 ##### 4.1 同步工具 本节介绍了常用的同步工具及其使用方法: - **4.1.1 原子操作**:提供原子读写操作以简化同步逻辑。 - **4.1.2 内存...

    多线程编程技术《Programming Multithreaded applications

    对象锁是对特定对象的锁定,用于保护对象的状态不被多个线程同时修改。 ##### 初始化和线程安全 确保程序初始化阶段正确处理线程安全问题是至关重要的,特别是在使用全局变量和静态变量时。 ##### 线程的特有数据...

    MFC多线程 工作者线程 用户界面线程

    使用MFC的同步对象(如C CriticalSection)可以确保对共享数据的独占访问。 六、调试多线程程序 在开发和调试多线程程序时,可以利用MFC提供的afxThreadState类来跟踪当前线程的状态,以及使用Visual Studio的调试...

    深入多线程编程

    2. **ST2MO (Simple Thread to Multiple Objects)**:适用于网络框架,通过对象池和超时管理器来协调多个线程和对象之间的交互。 3. **SO2MT (Single Object to Multiple Threads)**:一种异步化和解耦合的模式,...

    来自IBM的(POSIX).

    线程是进程内的一个执行单元,同一进程中的多个线程共享进程的资源,如内存空间。 ##### 1.3.1 线程的分类 - **用户线程**: 完全由应用程序控制,操作系统不可见。 - **内核线程**: 直接由操作系统管理和调度。 ##...

    Delphi多线程教程

    `Synchronize`方法用于在一个线程中执行另一个线程的方法,这对于更新GUI非常重要。例如: ```delphi procedure TForm1.UpdateUI; begin Label1.Caption := '线程执行完成'; end; procedure TMyThread....

    嵌入式Linux应用程序开发详解-第9章(多线程编程)

    3. **并发执行**: 在多处理器系统中,多个线程可以在不同的处理器上并发执行,提高了系统的整体吞吐量。 4. **通信简单**: 由于线程共享进程的资源,因此线程间的通信比进程间通信更为简单直接。 ##### 9.1.2 线程...

    轻松学会JNI层多线程回调java方法

    多线程环境下的回调Java方法通常涉及线程同步,以确保数据安全。Java中的`JNIEnv*`指针是线程相关的,因此在多线程环境中,不能直接在新线程中使用它。为了解决这个问题,我们需要在新线程中创建一个新的JNI环境,并...

    android开发,多线程断点下载

    在Android中,实现多线程断点下载通常涉及以下几个步骤: 1. **文件分块**:根据文件大小将其分成若干个块,每个块对应一个下载线程。 2. **创建线程池**:使用`ExecutorService`来管理下载线程,可以灵活调整线程...

    多线程与socket

    在c语言Socket编程(专业排版0积分).doc中,可能会详细讲解C语言实现Socket编程的语法和技巧。 总的来说,这个教程组合提供了从基础到高级的多线程和Socket编程知识,适合对网络编程感兴趣的C/C++开发者学习。通过...

    C/C++程序设计导论(第二版)附书源码

    根据提供的文件信息,“C/C++程序设计导论(第二版)附书源码”,我们可以从中提炼出以下几个重要的知识点: ### 一、C/C++语言概述 #### 1. C语言简介 - **历史背景**:C语言是由贝尔实验室的丹尼斯·里奇于1972年...

    vc面试题

    从给定的文件信息中,我们可以提取到几个与IT领域,特别是编程和软件工程相关的知识点。下面将对这些知识点进行详细的解析。 ### 面试题分析:经典虚拟函数调用 在描述中提到的“几道经典的面试题”,首先涉及的是...

Global site tag (gtag.js) - Google Analytics