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 (1000); // 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... endCLR確保一次僅一個線程能夠執行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 (1000); // 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 (1000); 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。
當重入變得危險時,有時可以使用幾個選項。如,假設在一個同步類內實現了多線程,通過委托這個邏輯到工作線程運行在一個獨立的上下文中。這些工作線程可能是不合理的妨礙了彼此間或者與非再入性的原始對象之間的的通訊。
這突出了自動同步一個基本弱點:大范圍的應用鎖可能制造很多困難,但也可能不會出現困難。這些困難如死鎖,重入及被閹割的并發性,使得在一些簡單的場景中手動加鎖比起它更加有優勢。
?
轉載于:https://www.cnblogs.com/C-Sharp2/p/4252777.html
總結
以上是生活随笔為你收集整理的C#学习笔记之线程 - 同步上下文的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常用的两个批处理
- 下一篇: osgi实战学习之路:8. Servic