C#学习笔记之线程 - 同步上下文
同步上下文(Synchronization Contexts)
手动使用锁的一个替代方案是去声明锁。通过派生ContextBoundObject和应用Synchronization属性,你告诉CLR自动加锁。
using System;
using System.Threading;
using System.Runtime.Remoting.Contexts;
[Synchronization]
public class AutoLock : ContextBoundObject
{
public void Demo()
{
Console.Write ("Start...");
Thread.Sleep (); // We can't be preempted here
Console.WriteLine ("end"); // thanks to automatic locking!
}
}
public class Test
{
public static void Main()
{
AutoLock safeInstance = new AutoLock();
new Thread (safeInstance.Demo).Start(); // Call the Demo
new Thread (safeInstance.Demo).Start(); // method 3 times
safeInstance.Demo(); // concurrently.
}
}
/// Output
Start... end
Start... end
Start... end
CLR确保一次仅一个线程能够执行SafeInstance的代码。它是通过创建一个同步对象来实现的--并且围绕着每一个SafeInstance的方法和属性加锁。锁的范围--在这个例子中,是safeINstance对象--也称为同步上下文。
那么这是如何工作的呢?在Synchronization属性的命名空间中有一体线索:System.Runtime.Remoting.Contexts。
ContextBoundObject可以被认为是一个远程(Remote)对象,意味着所有方法的调用被拦截。为了使他可以被拦截,当我们实例化AutoLock时,CLR实际返回了一个代理--带有与AutoLock有相同方法和属性的对象,作为中间人。通过这个中间人自动加锁,总之,它围绕每一个函数调用增加了一个微妙。
自动同步不能被用于静态成员类型,也不能用于不是从ContextBoundObject中派生的类(如Window Form)。
在内部锁以同样的方式使用。你可以预期下面的例子与上面的有相同的结果:
using System;
using System.Threading;
using System.Runtime.Remoting.Contexts;
[Synchronization]
public class AutoLock : ContextBoundObject
{
public void Demo()
{
Console.Write ("Start...");
Thread.Sleep (); // We can't be preempted here
Console.WriteLine ("end"); // thanks to automatic locking!
} public void Test()
{
new Thread (Demo).Start(); // Call the Demo
new Thread (Demo).Start(); // method 3 times
new Thread (Demo).Start(); // method 3 times
Console.ReadLine();
} public static void Main()
{new AutoLock().Test();}
}
/// Output
Start... end
Start... end
Start... end
(注意我们加了Console.ReadLine()语句)。因为一次只能一个线程执行,因此3个新的线程将阻塞在Demo上直到Test返回--它要求ReadLine。因此,我们有相同的结果,但是必须在按下Enter之后。这个线程安全的锤子足够大以致能妨碍类里的其它非常有用的线程。
进一步,我们并没有解决前面提到的问题:如果AutoLock是一个集合类,我们仍然要求围绕下面的语句加锁,假设它允许在另外一个类上:if(safeInstance.Count>0)safeInstance.RemoeAt(0);除非这个类本身的代码是一个同步的ContextBoundObject!
一个同步对象能被扩展到单一对象的范围之外。默认,如果同步对象是从另外一个类的代码中实例化,那么2者有相同的上下文(也就是说,一个更大的锁)。这种行为可以通过指定Synchronization属性的一个整数标记来改变,下面就是标记的常量:
| Constant | Meaning |
| NOT_SUPPORTED | Equivalent to not using the Synchronized attribute |
| SUPPORTED | Joins the existing synchronization context if instantiated from another synchronized object, otherwise remains unsynchronized. |
|
REQUIRED (DEFAULT) |
Joins the xisting synchronization context if instantiated from another synchronized object, otherwise creates a new context. |
| REQUIRES_NEW | Always creates a new synchronization context. |
所以,如果SynchronizedA实例化一个SynchronizedB对象,那么他们有不同的同步上下文,如果它们象下面那样声明:
[Synchronization(SynchronizationAttribute.REQUIRES_NEW)]
public class SynchronizedB : ContextBoundObject{...}
同步上下文的范围越大,越容易去管理,但是用于并发的机会也越小。话又说回来,独立的同步上下文会引起死锁。如
[Synchronization]
public class Deadlock : ContextBoundObject
{
public DeadLock Other;
public void Demo() { Thread.Sleep (); Other.Hello(); }
void Hello() { Console.WriteLine ("hello"); }
}
public class Test
{
static void Main()
{
Deadlock dead1 = new Deadlock();
Deadlock dead2 = new Deadlock();
dead1.Other = dead2;
dead2.Other = dead1;
new Thread (dead1.Demo).Start();
dead2.Demo();
}
}
因为每一个DeadLock实例在Test内部创建--一个非同步类--每一个实例有它自己的同步上下文,因此,它拥有自己的锁。当2个对象彼此调用时,不用多久就会发生死锁(最多1秒钟)。如果DeadLock和Test是不同的团队写的,那么这个很难察觉。要负责Test类的人去意识到死锁是不切实际的,更不用说让他解决这个问题。与显式锁相比,死锁更加明显。
重入(Reentrancy)
一个线程安全的方法有时称为可再入,因为它可以被抢先执行,然后再次调用其它线程而不会有副作用。在通常情况下,线程安全和可再入的术语被认为是同义词或非常接近。
然而,重入在自动化加锁机制中有更多的内涵。如果Synchronization属性带有reentrant为true的参数被使用:[Synchronization[true]]
那么这个同步上下文的锁将被临时释放,当它离开上下文时。在前面的例子中,这将避免死锁的发生:这正是我们所要的。但是,这个期间的有一个副作用,任何线程可以自由在原始对象上调用任何方法(重入同步环境)并且释放
出了一开始想避免的并发的复杂性。这就是再入的问题。
因为[Synchronization(true)]应用在类级别,这个属性使得被类调用的每一个函数脱离上下文变成了Trojan。
当重入变得危险时,有时可以使用几个选项。如,假设在一个同步类内实现了多线程,通过委托这个逻辑到工作线程运行在一个独立的上下文中。这些工作线程可能是不合理的妨碍了彼此间或者与非再入性的原始对象之间的的通讯。
这突出了自动同步一个基本弱点:大范围的应用锁可能制造很多困难,但也可能不会出现困难。这些困难如死锁,重入及被阉割的并发性,使得在一些简单的场景中手动加锁比起它更加有优势。
C#学习笔记之线程 - 同步上下文的更多相关文章
- linux学习笔记之线程同步机制
一.基础知识. 1:线程同步机制:互斥量,读写锁,条件变量,自旋锁,屏障. 1,互斥量:每个进程访问被互斥量保护的资源时,都需要先对互斥量进行判断. 1)互斥量重要属性:进程共享属性,健壮属性,类型属 ...
- APUE学习笔记——11 线程同步、互斥锁、自旋锁、条件变量
线程同步 同属于一个进程的不同线程是共享内存的,因而在执行过程中需要考虑数据的一致性. 假设:进程有一变量i=0,线程A执行i++,线程B执行i++,那么最终i的取值是多少呢?似乎一定 ...
- APUE 学习笔记(八) 线程同步
1. 进程的所有信息对该进程内的所有线程都是共享的 包括 可执行的程序文本.程序全局内存.堆内存以及文件描述符 线程包含了表示进程内执行环境必需的信息,包括线程ID.寄存器值.栈.调度优先级和策略.信 ...
- Linux学习笔记21——线程同步的两种方式
一 用信号量同步 1 信号量函数的名字都以sem_开头,线程中使用的基本信号量函数有4个 2 创建信号量 #include<semaphore.h> int sem_init(sem_t ...
- 操作系统学习笔记----进程/线程模型----Coursera课程笔记
操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...
- JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析
JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- C++11并发学习之三:线程同步(转载)
C++11并发学习之三:线程同步 1.<mutex> 头文件介绍 Mutex又称互斥量,C++ 11中与 Mutex 相关的类(包括锁类型)和函数都声明在 <mutex> 头文 ...
- Java学习笔记之——线程的生命周期、线程同步
一. 线程的生命周期 新建(new Thrad):创建线程后,可以设置各个属性值,即启动前 设置 就绪(Runnable):已经启动,等待CPU调动 运行(Running):正在被CPU调度 阻塞(B ...
随机推荐
- NEUOJ 1117: Ready to declare(单调队列)
1117: Ready to declare 时间限制: 1 Sec 内存限制: 128 MB 提交: 358 解决: 41 [提交][状态][pid=1117" style=" ...
- Codeforces Round #330 (Div. 1) C. Edo and Magnets 暴力
C. Edo and Magnets Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/594/pr ...
- delphi 16 网页缩放
网页放大 网页缩小 WebBrowser1.OleObject.Document.Body.Style.Zoom := 0.50; 缩放网页 Ctrl+中键↑ 放大 Ctrl+中键↓ ...
- MyEclipse 8.5 Axis2 插件完整jar包
http://download.csdn.net/download/hob007/4457837 MyEclipse 8.5 安装 Axis2 插件所需的完整JAR包 两个axis2插件,以及三个ja ...
- Java中for循环以及循环中标签
1.第一种,通过迭代的方式 File[] listFiles = file.listFiles(); for (Iterator iterator = files.iterator(); iterat ...
- dubbo-RPC学习(二) http protocol
dubbo中的协议默认支持很多种,从简单的开始,先解读HttpProtocol吧. dubbo中的http-RPC基于spring web支持的http invoker,web容器默认使用jetty. ...
- Naive Bayes Algorithm
朴素贝叶斯的核心基础理论就是贝叶斯理论和条件独立性假设,在文本数据分析中应用比较成功.朴素贝叶斯分类器实现起来非常简单,虽然其性能经常会被支持向量机等技术超越,但有时也能发挥出惊人的效果.所以,在将朴 ...
- 浅谈C/C++中的static和extern关键字 转
原文:http://developer.51cto.com/art/201104/256820.htm static是C++中常用的修饰符,它被用来控制变量的存贮方式和可见性.extern, &quo ...
- vs2010 MFC执行流程
从大学时候学过到现在可能又要用到,搞这个东西真是痛苦.不过首先把繁琐的c++函数执行流程搞明白吧,好好多设置几个断点观测一下啦. Step1.在vs2010新建MFC工程,那么对应的代码页名字为:CS ...
- python(5)–datetime模块
1. datatime.date.today() 当前日期 >>> import datetime >>> datetime.date.today() da ...