在多线程中,为了使数据保持一致性必须要对数据或是访问数据的函数加锁,在数据库中这是很常见的,但是在程序中由于大部分都是单线程的程序,所以没有加锁的必要,但是在多线程中,为了保持数据的同步,一定要加锁,好在Framework中已经为我们提供了三个加锁的机制,分别是Monitor类、Lock关键字和Mutex类。
其中Lock关键词用法比较简单,Monitor类和Lock的用法差不多。这两个都是锁定数据或是锁定被调用的函数。而Mutex则多用于锁定多线程间的同步调用。简单的说,Monitor和Lock多用于锁定被调用端,而Mutex则多用锁定调用端。
例如下面程序:由于这种程序都是毫秒级的,所以运行下面的程序可能在不同的机器上有不同的结果,在同一台机器上不同时刻运行也有不同的结果,我的测试环境为vs2005, windowsXp , CPU3.0 , 1 G monery。
程序中有两个线程thread1、thread2和一个TestFunc函数,TestFunc会打印出调用它的线程名和调用的时间(mm级的),两个线程分别以30mm和100mm来调用TestFunc这个函数。TestFunc执行的时间为50mm。程序如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace MonitorLockMutex
{
class Program
{
#region variable
Thread thread1 = null;
Thread thread2 = null;
Mutex mutex = null;
#endregion
static void Main(string[] args)
{
Program p = new Program();
p.RunThread();
Console.ReadLine();
}
public Program()
{
mutex = new Mutex();
thread1 = new Thread(new ThreadStart(thread1Func));
thread2 = new Thread(new ThreadStart(thread2Func));
}
public void RunThread()
{
thread1.Start();
thread2.Start();
}
private void thread1Func()
{
for (int count = 0; count < 10; count++)
{
TestFunc("Thread1 have run " + count.ToString() + " times");
Thread.Sleep(30);
}
}
private void thread2Func()
{
for (int count = 0; count < 10; count++)
{
TestFunc("Thread2 have run " + count.ToString() + " times");
Thread.Sleep(100);
}
}
private void TestFunc(string str)
{
Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
Thread.Sleep(50);
}
}
}
运行结果如下:
可以看出如果不加锁的话,这两个线程基本上是按照各自的时间间隔+TestFunc的执行时间(50mm)对TestFunc函数进行读取。因为线程在开始时需要分配内存,所以第0次的调用不准确,从第1~9次的调用可以看出,thread1的执行间隔约是80mm,thread2的执行间隔约是150mm。
现在将TestFunc修改如下:
private void TestFunc(string str)
{
lock (this)
{
Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
Thread.Sleep(50);
}
}
或者是用Monitor也是一样的,如下:
private void TestFunc(string str)
{
Monitor.Enter(this);
Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
Thread.Sleep(50);
Monitor.Exit(this);
}
其中Enter和Exit都是Monitor中的静态方法。
运行Lock结果如下:
让我们分析一下结果,同样从第1次开始。相同线程间的调用时间间隔为线程执行时间+TestFunc调用时间,不同线程间的调用时间间隔为TestFunc调用时间。例如:连续两次调用thread1之间的时间间隔约为30+50=80;连续两次调用thread2之间的时间间隔约为100+50=150mm。调用thread1和thread2之间的时间间隔为50mm。因为TestFunc被lock住了,所以一个thread调用TestFunc后,当其它的线程也同时调用TestFunc时,后来的线程即进被排到等待队列中等待,直到拥有访问权的线程释放这个资源为止。
这就是锁定被调用函数的特性,即只能保证每次被一个线程调用,线程优先级高的调用的次数就多,低的就少,这就是所谓的强占式。
下面让我们看看Mutex类的使用方法,以及与Monitor和Lock的区别。
将代码修改如下:
private void thread1Func()
{
for (int count = 0; count < 10; count++)
{
mutex.WaitOne();
TestFunc("Thread1 have run " + count.ToString() + " times");
mutex.ReleaseMutex();
}
}
private void thread2Func()
{
for (int count = 0; count < 10; count++)
{
mutex.WaitOne();
TestFunc("Thread2 have run " + count.ToString() + " times");
mutex.ReleaseMutex();
}
}
private void TestFunc(string str)
{
Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
Thread.Sleep(50);
}
运行结果如下:
可以看出,Mutex只能互斥线程间的调用,但是不能互斥本线程的重复调用,即thread1中waitOne()只对thread2中的waitOne()起到互斥的作用,但是thread1并不受本wainOne()的影响,可以调用多次,只是在调用结束后调用相同次数的ReleaseMutex()就可以了。
那么如何使线程按照调用顺序来依次执行呢?其实把lock和Mutex结合起来使用就可以了,改代码如下:
private void thread1Func()
{
for (int count = 0; count < 10; count++)
{
lock (this)
{
mutex.WaitOne();
TestFunc("Thread1 have run " + count.ToString() + " times");
mutex.ReleaseMutex();
}
}
}
private void thread2Func()
{
for (int count = 0; count < 10; count++)
{
lock (this)
{
mutex.WaitOne();
TestFunc("Thread2 have run " + count.ToString() + " times");
mutex.ReleaseMutex();
}
}
}
- java基础知识回顾之java Thread类学习(六)--java多线程同步函数用的锁
1.验证同步函数使用的锁----普通方法使用的锁 思路:创建两个线程,同时操作同一个资源,还是用卖票的例子来验证.创建好两个线程t1,t2,t1线程走同步代码块操作tickets,t2,线程走同步函数 ...
- C#中Monitor类、Lock关键字和Mutex类
线程:线程是进程的独立执行单元,每一个进程都有一个主线程,除了主线程可以包含其他的线程.多线程的意义:多线程有助于改善程序的总体响应性,提高CPU的效率.多线程的应用程序域是相当不稳定的,因为多个线程 ...
- 多线程同步与并发访问共享资源工具—Lock、Monitor、Mutex、Semaphore
“线程同步”的含义 当一个进程启动了多个线程时,如果需要控制这些线程的推进顺序(比如A线程必须等待B和C线程执行完毕之后才能继续执行),则称这些线程需要进行“线程同步(thread synchro ...
- C#多线程(4):进程同步Mutex类
Mutex 类 构造函数和方法 系统只能运行一个程序的实例 解释一下上面的示例 接替运行 进程同步示例 另外 Mutex 类 Mutex 中文为互斥,Mutex 类叫做互斥锁.它还可用于进程间同步的同 ...
- Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)
多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...
- 线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁
当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.E ...
- 第十三节:实际开发中使用最多的监视锁Monitor、lock语法糖的扩展、混合锁的使用(ManualResetEvent、SemaphoreSlim、ReaderWriterLockSlim)
一. 监视锁(Monitor和lock) 1. Monitor类,限定线程个数的一把锁,两个核心方法: Enter:锁住某个资源. Exit:退出某一个资源. 测试案例:开启5个线程同时对一个变量进行 ...
- 锁、C#中Monitor和Lock以及区别
1.Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁 ...
- Java线程同步的Monitor机制(Lock配合Condition)
Monitor模式是一种常见的并行开发机制, 一个Monitor实例可以被多个线程安全使用, 所有的monitor下面的方法在运行时是互斥的, 这种互斥机制机制可以用于一些特性, 例如让线程等待某种条 ...
随机推荐
- dom 筛选器
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 实体框架 (EF) 入门 => 四、CodeFirst 枚举支持
当使用 Code First 开发时,通常是从编写用来定义概念(域)模型的 .NET Framework 类开始. 插入记录没有为 Budget 赋值. 数值类型默认值为0,数据库中都为not nul ...
- return View()
- HD4505小Q系列故事——电梯里的爱情
Problem Description 细心的同事发现,小Q最近喜欢乘电梯上上下下,究其原因,也许只有小Q自己知道:在电梯里经常可以遇到他心中的女神HR. 电梯其实是个很暧昧的地方,只有在电梯里,小Q ...
- A Dream
A Dream 2013年10月20日,成都,天气阴,铜牌16.离2012年10月14日长春现场赛刚好隔了一年,刚看了下去年写的总结http://blog.csdn.net/cc_again/arti ...
- 创建类模式(四):原型(Prototype)
定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建 ...
- 关于html和body高度 登录页面登录框永远保持在页面垂直水平居中位置
HTML CSS 解说,水平居中就不用多说了,给div.login_wrap设置个宽度,然后margin:0 auto;就可以做到.但是这里这样做,这里使用了两层div 外层使用margin:0 au ...
- urlrewritingnet 域名http状态302 问题(转)
UrlRewritingNet is an Url rewriting tool for ASP .Net and Elmahis a module for logging unhandled err ...
- MySQL 日期时间
NOW()函数以`'YYYY-MM-DD HH:MM:SS'返回当前的日期时间,可以直接存到DATETIME字段中.CURDATE()以’YYYY-MM-DD’的格式返回今天的日期,可以直接存到DAT ...
- 【汉字乱码】IE下GET形式传递汉字。
js:encodeURI(); php:urlencode(); 举例: 本来打算这样使用 <a href="list.php?plate=辖区动态" charset=&qu ...