亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C#線程同步的幾種方法總結

 更新時間:2020年02月19日 11:04:35   作者:森大科技  
在本篇文章里小編給大家整理的是關于C#線程同步的幾種方法總結,需要的朋友們可以學習下。

我們在編程的時候,有時會使用多線程來解決問題,比如你的程序需要在后臺處理一大堆數(shù)據(jù),但還要使用戶界面處于可操作狀態(tài);或者你的程序需要訪問一些外部資源如數(shù)據(jù)庫或網絡文件等。這些情況你都可以創(chuàng)建一個子線程去處理,然而,多線程不可避免地會帶來一個問題,就是線程同步的問題。如果這個問題處理不好,我們就會得到一些非預期的結果。

在網上也看過一些關于線程同步的文章,其實線程同步有好幾種方法,下面我就簡單的做一下歸納。

一、volatile關鍵字

volatile是最簡單的一種同步方法,當然簡單是要付出代價的。它只能在變量一級做同步,volatile的含義就是告訴處理器, 不要將我放入工作內存, 請直接在主存操作我。(【轉自www.bitsCN.com 】)因此,當多線程同時訪問該變量時,都將直接操作主存,從本質上做到了變量共享。

能夠被標識為volatile的必須是以下幾種類型:(摘自MSDN)

• Any reference type.
• Any pointer type (in an unsafe context).
• The types sbyte, byte, short, ushort, int, uint, char, float, bool.
• An enum type with an enum base type of byte, sbyte, short, ushort, int, or uint.

如:

public class A
{
private volatile int _i;
public int I
{
get { return _i; }
set { _i = value; }
}
} 

但volatile并不能實現(xiàn)真正的同步,因為它的操作級別只停留在變量級別,而不是原子級別。如果是在單處理器系統(tǒng)中,是沒有任何問題的,變量在主存中沒有機會被其他人修改,因為只有一個處理器,這就叫作processor Self-Consistency。但在多處理器系統(tǒng)中,可能就會有問題。 每個處理器都有自己的data cach,而且被更新的數(shù)據(jù)也不一定會立即寫回到主存。所以可能會造成不同步,但這種情況很難發(fā)生,因為cach的讀寫速度相當快,flush的頻率也相當高,只有在壓力測試的時候才有可能發(fā)生,而且?guī)茁史浅7浅P ?/p>

二、lock關鍵字

lock是一種比較好用的簡單的線程同步方式,它是通過為給定對象獲取互斥鎖來實現(xiàn)同步的。它可以保證當一個線程在關鍵代碼段的時候,另一個線程不會進來,它只能等待,等到那個線程對象被釋放,也就是說線程出了臨界區(qū)。用法:

public void Function() 
{
object lockThis = new object (); 
lock (lockThis)
{
// Access thread-sensitive resources. 
}
} 

ock的參數(shù)必須是基于引用類型的對象,不要是基本類型像bool,int什么的,這樣根本不能同步,原因是lock的參數(shù)要求是對象,如果傳入int,勢必要發(fā)生裝箱操作,這樣每次lock的都將是一個新的不同的對象。最好避免使用public類型或不受程序控制的對象實例,因為這樣很可能導致死鎖。特別是不要使用字符串作為lock的參數(shù),因為字符串被CLR“暫留”,就是說整個應用程序中給定的字符串都只有一個實例,因此更容易造成死鎖現(xiàn)象。建議使用不被“暫留”的私有或受保護成員作為參數(shù)。其實某些類已經提供了專門用于被鎖的成員,比如Array類型提供SyncRoot,許多其它集合類型也都提供了SyncRoot。

所以,使用lock應該注意以下幾點: 

1、如果一個類的實例是public的,最好不要lock(this)。因為使用你的類的人也許不知道你用了lock,如果他new了一個實例,并且對這個實例上鎖,就很容易造成死鎖。

2、如果MyType是public的,不要lock(typeof(MyType))

3、永遠也不要lock一個字符串

三、System.Threading.Interlocked

對于整數(shù)數(shù)據(jù)類型的簡單操作,可以用 Interlocked 類的成員來實現(xiàn)線程同步,存在于System.Threading命名空間。Interlocked類有以下方法:Increment , Decrement , Exchange 和CompareExchange 。使用Increment 和Decrement 可以保證對一個整數(shù)的加減為一個原子操作。Exchange 方法自動交換指定變量的值。CompareExchange 方法組合了兩個操作:比較兩個值以及根據(jù)比較的結果將第三個值存儲在其中一個變量中。比較和交換操作也是按原子操作執(zhí)行的。如:

int i = 0 ;
System.Threading.Interlocked.Increment( ref i);
Console.WriteLine(i);
System.Threading.Interlocked.Decrement( ref i);
Console.WriteLine(i);
System.Threading.Interlocked.Exchange( ref i, 100 );
Console.WriteLine(i);
System.Threading.Interlocked.CompareExchange( ref i, 10 , 100 );

Output:

四、Monitor

Monitor類提供了與lock類似的功能,不過與lock不同的是,它能更好的控制同步塊,當調用了Monitor的Enter(Object o)方法時,會獲取o的獨占權,直到調用Exit(Object o)方法時,才會釋放對o的獨占權,可以多次調用Enter(Object o)方法,只需要調用同樣次數(shù)的Exit(Object o)方法即可,Monitor類同時提供了TryEnter(Object o,[int])的一個重載方法,該方法嘗試獲取o對象的獨占權,當獲取獨占權失敗時,將返回false。

但使用 lock 通常比直接使用 Monitor 更可取,一方面是因為 lock 更簡潔,另一方面是因為 lock 確保了即使受保護的代碼引發(fā)異常,也可以釋放基礎監(jiān)視器。這是通過 finally 中調用Exit來實現(xiàn)的。事實上,lock 就是用 Monitor 類來實現(xiàn)的。下面兩段代碼是等效的:

lock (x)
{
DoSomething();
} 
等效于

object obj = ( object )x;
System.Threading.Monitor.Enter(obj);
try 
{
DoSomething();
}
finally 
{
System.Threading.Monitor.Exit(obj);
} 

關于用法,請參考下面的代碼:

private static object m_monitorObject = new object ();
[STAThread]
static void Main( string [] args)
{
Thread thread = new Thread( new ThreadStart(Do));
thread.Name = " Thread1 " ;
Thread thread2 = new Thread( new ThreadStart(Do));
thread2.Name = " Thread2 " ;
thread.Start();
thread2.Start();
thread.Join();
thread2.Join();
Console.Read();
}
static void Do()
{
if ( ! Monitor.TryEnter(m_monitorObject))
{
Console.WriteLine( " Can't visit Object " + Thread.CurrentThread.Name);
return ;
}
try 
{
Monitor.Enter(m_monitorObject);
Console.WriteLine( " Enter Monitor " + Thread.CurrentThread.Name);
Thread.Sleep( 5000 );
}
finally 
{
Monitor.Exit(m_monitorObject);
}
} 

當線程1獲取了m_monitorObject對象獨占權時,線程2嘗試調用TryEnter(m_monitorObject),此時會由于無法獲取獨占權而返回false,輸出信息如下:

另外,Monitor還提供了三個靜態(tài)方法Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用來實現(xiàn)一種喚醒機制的同步。關于這三個方法的用法,可以參考MSDN,這里就不詳述了。

五、Mutex

在使用上,Mutex與上述的Monitor比較接近,不過Mutex不具備Wait,Pulse,PulseAll的功能,因此,我們不能使用Mutex實現(xiàn)類似的喚醒的功能。不過Mutex有一個比較大的特點,Mutex是跨進程的,因此我們可以在同一臺機器甚至遠程的機器上的多個進程上使用同一個互斥體。盡管Mutex也可以實現(xiàn)進程內的線程同步,而且功能也更強大,但這種情況下,還是推薦使用Monitor,因為Mutex類是win32封裝的,所以它所需要的互操作轉換更耗資源。

六、ReaderWriterLock

在考慮資源訪問的時候,慣性上我們會對資源實施lock機制,但是在某些情況下,我們僅僅需要讀取資源的數(shù)據(jù),而不是修改資源的數(shù)據(jù),在這種情況下獲取資源的獨占權無疑會影響運行效率,因此.Net提供了一種機制,使用ReaderWriterLock進行資源訪問時,如果在某一時刻資源并沒有獲取寫的獨占權,那么可以獲得多個讀的訪問權,單個寫入的獨占權,如果某一時刻已經獲取了寫入的獨占權,那么其它讀取的訪問權必須進行等待,參考以下代碼:

private static ReaderWriterLock m_readerWriterLock = new ReaderWriterLock();
private static int m_int = 0;
[STAThread]
static void Main(string[] args)
{
Thread readThread = new Thread(new ThreadStart(Read));
readThread.Name = "ReadThread1";
Thread readThread2 = new Thread(new ThreadStart(Read));
readThread2.Name = "ReadThread2";
Thread writeThread = new Thread(new ThreadStart(Writer));
writeThread.Name = "WriterThread";
readThread.Start();
readThread2.Start();
writeThread.Start();
readThread.Join();
readThread2.Join();
writeThread.Join();

Console.ReadLine(); 
}
private static void Read()
{
while (true)
{
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireReaderLock");
m_readerWriterLock.AcquireReaderLock(10000);
Console.WriteLine(String.Format("ThreadName : {0} m_int : {1}", Thread.CurrentThread.Name, m_int));
m_readerWriterLock.ReleaseReaderLock();
}
}

private static void Writer()
{
while (true)
{
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireWriterLock");
m_readerWriterLock.AcquireWriterLock(1000);
Interlocked.Increment(ref m_int);
Thread.Sleep(5000);
m_readerWriterLock.ReleaseWriterLock();
Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " ReleaseWriterLock");
}
} 

在程序中,我們啟動兩個線程獲取m_int的讀取訪問權,使用一個線程獲取m_int的寫入獨占權,執(zhí)行代碼后,輸出如下:

可以看到,當WriterThread獲取到寫入獨占權后,任何其它讀取的線程都必須等待,直到WriterThread釋放掉寫入獨占權后,才能獲取到數(shù)據(jù)的訪問權,應該注意的是,上述打印信息很明顯顯示出,可以多個線程同時獲取數(shù)據(jù)的讀取權,這從ReadThread1和ReadThread2的信息交互輸出可以看出。

七、SynchronizationAttribute

當我們確定某個類的實例在同一時刻只能被一個線程訪問時,我們可以直接將類標識成Synchronization的,這樣,CLR會自動對這個類實施同步機制,實際上,這里面涉及到同步域的概念,當類按如下設計時,我們可以確保類的實例無法被多個線程同時訪問

1). 在類的聲明中,添加System.Runtime.Remoting.Contexts.SynchronizationAttribute屬性。

2). 繼承至System.ContextBoundObject
需要注意的是,要實現(xiàn)上述機制,類必須繼承至System.ContextBoundObject,換句話說,類必須是上下文綁定的。

一個示范類代碼如下:

[System.Runtime.Remoting.Contexts.Synchronization]
public class SynchronizedClass : System.ContextBoundObject
{

} 

八、MethodImplAttribute

如果臨界區(qū)是跨越整個方法的,也就是說,整個方法內部的代碼都需要上鎖的話,使用MethodImplAttribute屬性會更簡單一些。這樣就不用在方法內部加鎖了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在命名空間System.Runtime.CompilerServices 里面。但要注意這個屬性會使整個方法加鎖,直到方法返回,才釋放鎖。因此,使用上不太靈活。如果要提前釋放鎖,則應該使用Monitor或lock。我們來看一個例子:

[MethodImpl(MethodImplOptions.Synchronized)]
public void DoSomeWorkSync()
{
Console.WriteLine( " DoSomeWorkSync() -- Lock held by Thread " + 
Thread.CurrentThread.GetHashCode());
Thread.Sleep( 1000 );
Console.WriteLine( " DoSomeWorkSync() -- Lock released by Thread " + 
Thread.CurrentThread.GetHashCode());
}
public void DoSomeWorkNoSync()
{
Console.WriteLine( " DoSomeWorkNoSync() -- Entered Thread is " + 
Thread.CurrentThread.GetHashCode());
Thread.Sleep( 1000 );
Console.WriteLine( " DoSomeWorkNoSync() -- Leaving Thread is " + 
Thread.CurrentThread.GetHashCode());
}

[STAThread]
static void Main( string [] args)
{
MethodImplAttr testObj = new MethodImplAttr();
Thread t1 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
Thread t2 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
t1.Start();
t2.Start();
Thread t3 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
Thread t4 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
t3.Start();
t4.Start();

Console.ReadLine(); 
} 

這里,我們有兩個方法,我們可以對比一下,一個是加了屬性MethodImpl的DoSomeWorkSync(),一個是沒加的DoSomeWorkNoSync()。在方法中Sleep(1000)是為了在第一個線程還在方法中時,第二個線程能夠有足夠的時間進來。對每個方法分別起了兩個線程,我們先來看一下結果:

可以看出,對于線程1和2,也就是調用沒有加屬性的方法的線程,當線程2進入方法后,還沒有離開,線程1有進來了,這就是說,方法沒有同步。我們再來看看線程3和4,當線程3進來后,方法被鎖,直到線程3釋放了鎖以后,線程4才進來。

九、同步事件和等待句柄

用lock和Monitor可以很好地起到線程同步的作用,但它們無法實現(xiàn)線程之間傳遞事件。如果要實現(xiàn)線程同步的同時,線程之間還要有交互,就要用到同步事件。同步事件是有兩個狀態(tài)(終止和非終止)的對象,它可以用來激活和掛起線程。

同步事件有兩種:AutoResetEvent和 ManualResetEvent。它們之間唯一不同的地方就是在激活線程之后,狀態(tài)是否自動由終止變?yōu)榉墙K止。AutoResetEvent自動變?yōu)榉墙K止,就是說一個AutoResetEvent只能激活一個線程。而ManualResetEvent要等到它的Reset方法被調用,狀態(tài)才變?yōu)榉墙K止,在這之前,ManualResetEvent可以激活任意多個線程。

可以調用WaitOne、WaitAny或WaitAll來使線程等待事件。它們之間的區(qū)別可以查看MSDN。當調用事件的 Set方法時,事件將變?yōu)榻K止狀態(tài),等待的線程被喚醒。

來看一個例子,這個例子是MSDN上的。因為事件只用于一個線程的激活,所以使用 AutoResetEvent 或 ManualResetEvent 類都可以。

static AutoResetEvent autoEvent;

static void DoWork()
{
Console.WriteLine(" worker thread started, now waiting on event ");
autoEvent.WaitOne();
Console.WriteLine(" worker thread reactivated, now exiting ");
}

[STAThread]
static void Main(string[] args)
{
autoEvent = new AutoResetEvent(false);

Console.WriteLine("main thread starting worker thread ");
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();

Console.WriteLine("main thrad sleeping for 1 second ");
Thread.Sleep(1000);

Console.WriteLine("main thread signaling worker thread ");
autoEvent.Set();

Console.ReadLine(); 
} 

我們先來看一下輸出:

在主函數(shù)中,首先創(chuàng)建一個AutoResetEvent的實例,參數(shù)false表示初始狀態(tài)為非終止,如果是true的話,初始狀態(tài)則為終止。然后創(chuàng)建并啟動一個子線程,在子線程中,通過調用AutoResetEvent的WaitOne方法,使子線程等待指定事件的發(fā)生。然后主線程等待一秒后,調用AutoResetEvent的Set方法,使狀態(tài)由非終止變?yōu)榻K止,重新激活子線程。

以上就是小編給大家整理的全部相關知識點,感謝你的支持。

相關文章

  • C# 系統(tǒng)熱鍵注冊實現(xiàn)代碼

    C# 系統(tǒng)熱鍵注冊實現(xiàn)代碼

    簡單點說就是為程序制定快捷鍵勒。。很多軟件都帶熱鍵功能的,通過以下方式可以實現(xiàn)2個鍵或3個鍵的快捷鍵,相當之使用,具體實現(xiàn)方法看后文吧。
    2009-02-02
  • 帶你一文了解C#中的LINQ

    帶你一文了解C#中的LINQ

    c#提供的ling查詢極大的遍歷了集合的查詢過程,且使用簡單方便,非常的有用,下面這篇文章主要給大家介紹了關于C#中LINQ的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-12-12
  • C#基礎之泛型委托實例教程

    C#基礎之泛型委托實例教程

    這篇文章主要介紹了C#中的泛型委托,并以EventHandler為例對泛型委托的常見用法進行了實例分析,需要的朋友可以參考下
    2014-09-09
  • c#結構和類的相關介紹

    c#結構和類的相關介紹

    結構和類的共同點都是屬于抽象數(shù)據(jù)類型,包含數(shù)據(jù)和數(shù)據(jù)的操作。不同點在于結構偏重于數(shù)據(jù)語意,而類偏重於行為語意。
    2012-12-12
  • C#操作Byte數(shù)組和十六進制進行互轉

    C#操作Byte數(shù)組和十六進制進行互轉

    這篇文章介紹了C#操作Byte數(shù)組和十六進制進行互轉的的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • c# 使用計時器和觀察者模式實現(xiàn)報警推送需求

    c# 使用計時器和觀察者模式實現(xiàn)報警推送需求

    這篇文章主要介紹了c# 使用計時器和觀察者模式實現(xiàn)報警推送需求,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • C#關鍵字之重寫override介紹

    C#關鍵字之重寫override介紹

    這篇文章介紹了C#關鍵字之重寫override,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 淺析C#中數(shù)組,ArrayList與List對象的區(qū)別

    淺析C#中數(shù)組,ArrayList與List對象的區(qū)別

    在C#中,當我們想要存儲一組對象的時候,就會想到用數(shù)組,ArrayList,List這三個對象了。那么這三者到底有什么樣的區(qū)別呢
    2013-07-07
  • C#多線程系列之線程通知

    C#多線程系列之線程通知

    本文詳細講解了C#多線程中的線程通知,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • implicit關鍵字做自定義類型隱式轉換的方法

    implicit關鍵字做自定義類型隱式轉換的方法

    implicit 關鍵字用于聲明隱式的用戶定義類型轉換運算符。如果轉換過程可以確保不會造成數(shù)據(jù)丟失,則可使用該關鍵字在用戶定義類型和其他類型之間進行隱式轉換,這篇文章就給大家詳細介紹implicit關鍵字做自定義類型隱式轉換的方法,需要的朋友可以參考下
    2015-08-08

最新評論