.NET基础拾遗(7)多线程开发基础4
一.多线程编程中的线程同步
1.C#中的lock关键字
lock关键字可能是我们在遇到线程同步的需求时最常用的方式,但lock只是一个语法糖,为什么这么说呢,下面慢慢道来。
(1)lock的等效代码其实是Monitor类的Enter和Exit两个方法
private object locker = new object();
public void Work()
{
lock (locker)
{
// 做一些需要线程同步的工作
}
} private object locker = new object();
public void Work()
{
// 避免直接使用私有成员locker(直接使用有可能会导致线程不安全)
object temp = locker;
Monitor.Enter(temp);
try
{
// 做一些需要线程同步的工作
}
finally
{
Monitor.Exit(temp);
}
}
(2)System.Threading.Monitor类型的作用和使用
Monitor类型的Enter和Exit方法用来实现进入和退出对象的同步,当Enter方法被调用时,对象的同步索引将被检查,并且.NET将负责一系列的后续工作来保证对象访问时的线程同步,而Exit方法的调用则保证了当前线程释放该对象的同步块。
示例演示了如何使用lock关键字来实现线程同步:
class Program
{
static void Main(string[] args)
{
// 多线程测试静态方法的同步
Console.WriteLine("开始测试静态方法的同步:");
for (int i = 0; i < 5; i++)
{
Thread thread = new Thread(Lock.StaticIncrement);
thread.Start();
}
// 这里等待线程执行结束
Thread.Sleep(5 * 1000);
Console.WriteLine("-------------------------------");
// 多线程测试实例方法的同步
Console.WriteLine("开始测试实例方法的同步:");
Lock l = new Lock();
for (int i = 0; i < 6; i++)
{
Thread thread = new Thread(l.InstanceIncrement);
thread.Start();
} Console.ReadKey();
}
} public class Lock
{
// 静态方法同步锁
private static object staticLocker = new object();
// 实例方法同步锁
private object instanceLocker = new object(); // 成员变量
private static int staticNumber = 0;
private int instanceNumber = 0; // 测试静态方法的同步
public static void StaticIncrement(object state)
{
lock (staticLocker)
{
Console.WriteLine("当前线程ID:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
Console.WriteLine("staticNumber的值为:{0}", staticNumber.ToString());
// 这里可以制造线程并行执行的机会,来检查同步的功能
Thread.Sleep(200);
staticNumber++;
Console.WriteLine("staticNumber自增后为:{0}", staticNumber.ToString());
}
} // 测试实例方法的同步
public void InstanceIncrement(object state)
{
lock (instanceLocker)
{
Console.WriteLine("当前线程ID:{0}",Thread.CurrentThread.ManagedThreadId.ToString());
Console.WriteLine("instanceNumber的值为:{0}", instanceNumber.ToString());
// 这里可以制造线程并行执行的机会,来检查同步的功能
Thread.Sleep(200);
instanceNumber++;
Console.WriteLine("instanceNumber自增后为:{0}", instanceNumber.ToString());
}
}
}

PS: 应该完全避免用this对象和当前类型对象作为同步对象,而是在类型中定义私有的同步对象,
同时应该使用lock而不是Monitor类型,这样可以有效地减少同步块不被释放的情况。
2. 互斥体是个什么鬼?Mutex和Monitor两个类型的功能有啥区别?
(1)什么是互斥体?
在操作系统中,互斥体(Mutex)是指某些代码片段在任意时间内只允许一个线程进入。例如,正在进行一盘棋,任意时刻只允许一个棋手往棋盘上落子,这和线程同步的概念基本一致。
(2).NET中的互斥体
Mutex类是.NET中为我们封装的一个互斥体类型,和Mutex类似的还有Semaphore(信号量)等类型。下面的示例代码展示了Mutext类型的使用
class Program
{
const string testFile = "C:\\TestMutex.txt";
/// <summary>
/// 这个互斥体保证所有的进程都能得到同步
/// </summary>
static Mutex mutex = new Mutex(false, "TestMutex"); static void Main(string[] args)
{
//留出时间来启动其他进程
Thread.Sleep(3000);
DoWork();
mutex.Close();
Console.ReadKey();
} /// <summary>
/// 往文件里写连续的内容
/// </summary>
static void DoWork()
{
long d1 = DateTime.Now.Ticks;
mutex.WaitOne();
long d2 = DateTime.Now.Ticks;
Console.WriteLine("经过了{0}个Tick后进程{1}得到互斥体,进入临界区代码。", (d2 - d1).ToString(), Process.GetCurrentProcess().Id.ToString()); try
{
if (!File.Exists(testFile))
{
FileStream fs = File.Create(testFile);
fs.Dispose();
}
for (int i = 0; i < 5; i++)
{
// 每次都保证文件被关闭再重新打开
// 确定有mutex来同步,而不是IO机制
using (FileStream fs = File.Open(testFile, FileMode.Append))
{
string content = "【进程" + Process.GetCurrentProcess().Id.ToString() +
"】:" + i.ToString() + "\r\n";
Byte[] data = Encoding.Default.GetBytes(content);
fs.Write(data, 0, data.Length);
}
// 模拟做了其他工作
Thread.Sleep(300);
}
}
finally
{
mutex.ReleaseMutex();
}
}
}
模拟多个用户,执行上述代码,下图就是在我的计算机上的执行结果:

现在打开C盘目录下的TestMutext.txt文件,将看到如下图所示的结果:

(3)Mutex和Monitor的区别
这两者虽然都用来进行同步的功能,但实现方法不同,其最显著的两个差别如下:
① Mutex使用的是操作系统的内核对象,而Monitor类型的同步机制则完全在.NET框架之下实现,这就导致了Mutext类型的效率要比Monitor类型要低很多;
② Monitor类型只能同步同一应用程序域中的线程,而Mutex类型却可以跨越应用程序域和进程。
.NET基础拾遗(7)多线程开发基础4的更多相关文章
- .NET基础拾遗(5)多线程开发基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- .NET基础拾遗(7)多线程开发基础2
二..NET中的多线程编程 2.1 如何在.NET程序中手动控制多个线程? 最直接且灵活性最大的,莫过于主动创建.运行.结束所有线程. (1)第一个多线程程序 .NET提供了非常直接的控制线程类型的类 ...
- .NET基础拾遗(7)多线程开发基础1
一.多线程编程的基本概念 1.1 操作系统层面的进程和线程 (1)进程 进程代表了操作系统上运行着的一个应用程序.进程拥有自己的程序块,拥有独占的资源和数据且可以被操作系统调度. But,即使是同一个 ...
- .NET基础拾遗(7)多线程开发基础3
一.如何使用异步模式? 异步模式是在处理流类型时经常采用的一种方式,其应用的领域相当广阔,包括读写文件.网络传输.读写数据库,甚至可以采用异步模式来做任何计算工作.相对于手动编写线程代码,异步模式是一 ...
- (转).NET基础拾遗(5)多线程开发基础
https://www.cnblogs.com/edisonchou/p/4848131.html
- ios多线程开发基础
多线程编程:下载数据时,开辟子线程,减少阻塞时间,和主线程并发运行,提升用户体验 1.Thread 1>新建Thread对象,带一selector方法,调用start方法,开启子线程 2> ...
- .NET基础拾遗(6)ADO.NET与数据库开发基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- .NET基础拾遗(7)Web Service的开发与应用基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- .NET基础拾遗(1)类型语法基础和内存管理基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
随机推荐
- iOS实现OAuth2.0中刷新access token并重新请求数据操作
一.简要概述 OAuth2.0是OAuth协议的下一版本,时常用于移动客户端的开发,是一种比较安全的机制.在OAuth 2.0中,server将发行一个短有效期的access token和长生命期的r ...
- oracle单行函数之通用函数
NVL (a,b) --当a=null时,返回b,否则返回a NVL2 (a, b, c) -- 当a=null时,返回c,否则返回b NULLIF (expr1, expr2) --当a=b时,返回 ...
- log4j的配置及使用
用日志的好处: 可以长久的保存日志信息. 日志可以保存到:网络.文件.数据库 设置日志的级别. OFF Fatal – System.exit(0); - JVM, ERROR – 错误,模块错误. ...
- 解决secureCRT数据库里没有找到防火墙 '无'问题,转自:http://jingyan.baidu.com/article/9989c74601274bf649ecfe74.html
中文版的secureCRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没有找到防火墙 '无' 此会话将尝试不通过防火墙进行连接.出现这个错误的原因是在secu ...
- 2014年企业改善IT风险管理的5个办法
进入新的一年,企业面对数据泄密事故.日益复杂的攻击和对其控制的持续监管,现在是时候重新审视其风险管理战略了.虽然每个企业都是独特的,风险管理专家认为有些风险管理办法值得企业关注.下面我们列出了5个风险 ...
- 《Javascript模式》之对象创建模式读书笔记
引言: 在javascript中创建对象是很容易的,可以使用对象字面量或者构造函数或者object.creat.在接下来的介绍中,我们将越过这些方法去寻求一些其他的对象创建模式. 我们知道js是一种简 ...
- php的冒泡算法
<?php /* 冒泡算法 * @para $arr 传人进去排序的数组 * @return $newArr 排序之后的数组 */ function maopao($arr){ ...
- css图片磨砂化
Css代码: .blur { filter: url(blur.svg#blur); /* FireFox, Chrome, Opera */ -webkit-filter: blur(10px); ...
- PHP自动添加http://头 转换网址为链接
有时候,当我们需要用户输入网址的时候,一般我们会让用户省略掉"http://",当提交完成后用代码自动再加上http://,若有需要,我们 还可将网址转换成链接的形式,类似于众多网 ...
- Java在ACM中的使用
1.基本框架 import java.oi.*; import java.util.* public class Main { public static void main(St ...