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 ...
随机推荐
- UVa540 Team Queue
// 题意:有t个团队的人在排队.每次来了一个新人之后,如果他有队友在排队,那么这个新人会插队到队友的身后. // 要求支持三种指令:ENQUEUE x; DEQUEUE(队首出队); STOP.模拟 ...
- Uestc_suibian 暑假集训总结
唉,终于组队了,终于可以只BB了,我就BB,我就不上! 和Xiper以及chenxh组队了- 下面是总结: day1 第一天吃饱喝足,然后就上路了,我一开始就看到了C题大水题,但是我不敢想象这道题居然 ...
- 密码强度应用(js)
<!-- 密码强度div --> <div id="tips" class="help-block"> <b class=&quo ...
- [转载]Android开发常用调试技术记录
ANDROID 调试技术: 1)Ps 指令 ls –l /proc/27/ cat /proc/27/cmdline #cmdline文件表示了这个进程所在的命令行. cat /proc/ ...
- [MEAN Stack] First API -- 5. Using $resource to setup REST app
Front-end changes: app.js: Uinsg $resource /** * Created by Answer1215 on 12/9/2014. */ 'use strict' ...
- 一个公网地址部署LVS/DR模式
http://blog.chinaunix.net/uid-7411781-id-3436142.html 一个公网地址部署LVS/DR模式 网上看了很多关于LVS的文章,在选取2种模式LVS/D ...
- 【ZZ】一张图清晰追溯数据库的发展历程(1962-2016年)
http://www.cbdio.com/BigData/2016-02/24/content_4651751.htm 历史发展概述
- jquery.validate.js使用id验证控件
jquery.validate.js默认的是元素的name. 例如:<input name="username" id="username" size=& ...
- 如何在有实体键的情况下全部显示ActionBar的Menu?
大伙都知道, 在老版本手机, 以及部分的新手机上都还残留实体键, 有了这些实体键, 默认菜单是用实体菜单键呼出的, 尽管你把android:showAsAction="always" ...
- Toad for Oracle 12.1下载地址
32 位版: http://us-downloads.quest.com/Repository/support.quest.com/Toad for Oracle/12.1/Software/Toad ...