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 ...
随机推荐
- C语言高效编程的几招(绝对实用,绝对经典)
编写高效简洁的C语言代码,是许多软件工程师追求的目标.废话不说,走起! 第一招:以空间换时间 计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题 eg.字符串的 ...
- flex-mp3
Mp3Player.as package ddw.adobe { import flash.events.Event; import flash.events.IOErrorEvent; import ...
- Codeforces Round #327 (Div. 2) E. Three States BFS
E. Three States Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/591/probl ...
- Codeforces Gym 100425A Luggage Distribution 二分 数学
A - Luggage DistributionTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/c ...
- Spring的DataSource配置、将Hibernate配置所有写到Spring配置
DataSource能够集中管理数据库连接,降低维护工作量,使部署更简单: Spring的DataSource配置:(Spring数据源配置)这里使用dbcp,还有非常多其它的如c3p0,jdbc,j ...
- Android4.4 蓝牙源码部分分析
最近GOOGLE发布了Android4.4,看了一下源码:4.4的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点 ...
- setAnimationTransition:forView:cache: 运行动画时背景色问题
首先我描写叙述一下问题:当我从一个view到另外一个view的时候? 解答:这个问题的解决还须要看setAnimationTransition:forView:cache: 官方Api,官方是这样说的 ...
- 统计0到n之间1的个数
问题描写叙述 给定一个十进制整数N,求出从1到N的全部整数中出现"1"的个数. 比如:N=2时 1,2出现了1个 "1" . N=12时 1,2,3,4,5,6 ...
- 使用asp.net动态添加html元素
HtmlGenericControl gen = new HtmlGenericControl("div"); gen.InnerText = "HtmlG ...
- SQL Server压缩日志及数据库文件大小
请按步骤进行,未进行前面的步骤时,请不要做后面的步骤,以免损坏你的数据库. 一般不建议做第4,6两步,第4步不安全,有可能损坏数据库或丢失数据.第6步如果日志达到上限,则以后的数据库处理会失败,在清理 ...