C#中線程同步對象的辦法剖析。本站提示廣大學習愛好者:(C#中線程同步對象的辦法剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中線程同步對象的辦法剖析正文
本文實例講述了C#中線程同步對象的辦法。分享給年夜家供年夜家參考。詳細剖析以下:
在編寫多線程法式時無可防止會碰到線程的同步成績。甚麼是線程的同步呢?
舉個例子:假如在一個公司外面有一個變量記載或人T的工資count=100,有兩個主管A和B(即任務線程)在早一些時刻拿了這個變量的值歸去,過了一段時光A主管將T的工資加了5塊,並存回count變量,而B主管將T的工資減去3塊,並存回count變量。好了,原來T君可以獲得102塊的工資的,如今就釀成98塊了。這就是線程同步要處理的成績。
在.Net的某些對象外面,在讀取外面的數據的同時還可以修正數據,這類的對象就是“線程平安”。但關於本身編寫的代碼段而言,就必需應用線程同步技巧來包管數據的完全性和准確性了。
有幾個紀律:
1、假如一個對象(或變量)不會同時被多個其他線程拜訪,那末這個對象是不需應用線程同步的。
2、假如固然有多個線程同時拜訪一個對象,但他們所拜訪的數據或辦法其實不雷同(不穿插),那這類情形也不需應用線程同步。
例如上例中的誰人公司外面假如有 T 和 Q 兩小我,但他們的工資分離是由 A 和 B 主管的,那末這個工資的處置就不須要線程同步了。
3、假如一個對象會同時被多個其他線程拜訪,普通只需為這個對象添加線程同步的代碼,而其他線程是不需添加額定代碼的。
在C#外面用於完成線程同步的經常使用類有以下幾類
1、Mutex類(互斥器),Monitor類,lock辦法
2、ManualResetEvent類,AutoResetEvent類(這兩個都是由EventWaitHandle類派生出來的)
3、ReaderWriterLock類
統一類的感化都差不多:個中第一類的感化是:用來掩護某段代碼在履行的時刻以獨有的方法履行,這時候假如有第二個線程想拜訪這個對象時就會被暫停。一向比及獨有的代碼履行為止。就比如一堆人同時上一個公共茅廁一樣,應用這個辦法便可以處理文章一開端時提出的成績:主管A要處置T君的工資之前,先lock一下T君,然後掏出今朝的count值,處置完以後再消除T君的鎖定。假如主管B在主管A處置工資時也想掏出count值,那末它只能是一向地期待A處置完以後能力持續。應用這個辦法的一個缺陷就是會下降法式的效力。原來是一個多個線程的操作,一旦碰到lock的語句時,那末這些線程只需列隊處置,形統一個單線程操作。
上面舉個例子解釋一下這三個辦法的應用:
假定有一個Tools類,外面一個int變量,還有Add和Delete辦法,個中Add辦法會使int變量的值增長,Delete辦法使int變量值削減:
public class Tools
{
private int count = 100;
public void Add(int n)
{
count+=n;
}
public void Delete(int n)
{
count-=n;
}
}
在多個線程同時拜訪這段代碼時,由於一個語句會被編譯器編譯成多個指令,所以會能夠湧現這類情形:但某個線程挪用Add辦法時,這時候的count值為 100,而合法要加上n的時刻,別的一個線程挪用了Delete,它要減去m,成果count加上了n,然後又在本來count=100的值的情形
下減失落了m,最初的成果是count被減去了m,而沒有加上n。很顯著Add辦法和Delete辦法是不克不及同時被挪用的,所以必需停止線程同步處置。簡略的辦法是用lock語句:
public class Tools
{
private object abcde = new object();
private int count = 100;
public void Add(int n)
{
lock(abcde)
{
count+=n;
}
}
public void Delete(int n)
{
lock(abcde)
{
count-=n;
}
}
}
個中abcde是一個private級的外部變量,它不表現任何的意義,只是作為一種“令牌”的腳色。
當履行Add辦法中的lock(abcde)辦法時,這個令牌就在Add辦法的手中了,假如這時候有第二個線程也想拿這個令牌,沒門,唯有期待。一旦第一個lock語句的花括號規模停止以後,這時候令牌就被釋放了,同時會敏捷落到第二個線程的手中,而且消除其他後來的人。
應用Monitor類的辦法年夜致一樣:
public class Tools
{
private object abcde = new object();
private int count = 100;
public void Add(int n)
{
Monitor.Enter(abcde);
count+=n;
Monitor.Exit(abcde);
}
public void Delete(int n)
{
Monitor.Enter(abcde);
count-=n;
Monitor.Exit(abcde);
}
}
Monitor的經常使用辦法:Enter和Exit都是靜態辦法,感化跟lock語句的兩個花括號一樣。
而應用 Mutex 就不需聲明一個“令牌”對象了,但要實例化以後才可使用:
public class Tools
{
private Mutex mut = new Mutex();
private int count = 100;
public void Add(int n)
{
mut.WaitOne();
count+=n;
mut.ReleaseMutex();
}
public void Delete(int n)
{
mut.WaitOne();
count-=n;
mut.ReleaseMutex();
}
}
個中的WaitOne為期待辦法,一向比及Mutex 被釋放為止。初始的情形下,Mutex 對象是處於釋放狀況的,而一旦履行了WaitOne辦法以後,它就被捕捉了,一向到被挪用了ReleaseMutex辦法以後才被釋放。
應用這三種辦法都有一個要留意的成績,就是在獨有代碼段外面假如惹起了異常,能夠會使“令牌”對象不被釋放,如許法式就會一向地逝世等下去了。
所以要在獨有代碼段外面處置好異常。例以下面如許的代碼就是毛病的:
public void Add(int n)
{
try
{
mut.WaitOne();
count+=n;
//....這裡省略了N行代碼
//....這裡是有能夠惹起異常的代碼
//....這裡省略了N行代碼
mut.ReleaseMutex();
}
catch
{
Console.Writeline("error.");
}
}
下面的代碼一旦在try和catch外面產生了異常,那末Mutex就不克不及被釋放,前面的法式就會卡逝世在WaitOne()一行,而應當改成如許:
public void Add(int n)
{
mut.WaitOne();
try
{
count+=n;
//....這裡省略了N行代碼
//....這裡是有能夠惹起異常的代碼
//....這裡省略了N行代碼
}
catch
{
Console.Writeline("error.");
}
mut.ReleaseMutex();
}
如今談一下第二種:
ManualResetEvent類,AutoResetEvent類
下面這兩個類都是由EventWaitHandle類派生出來的,所以功效和挪用辦法都很類似。
這兩個類經常使用於阻斷某個線程的履行,然後在相符前提的情形下再恢復其履行。
舉個例子,你想送花給一個MM,托了一個送花的小伙子送了曩昔,而你願望當MM收到花以後就立刻打個德律風曩昔告知她。
但成績是你不曉得花甚麼時刻才送到MM的手裡,打早了打遲了都欠好,這時候你可使用ManualResetEvent對象協助。當拜托小伙子送花曩昔的時刻,應用ManualResetEvent的WaitOne辦法停止期待。當小伙子把花送到MM的手中時,再挪用一下ManualResetEvent的Set辦法,你便可以准時地打德律風曩昔了。
別的ManualResetEvent還有一個Reset辦法,用來從新阻斷挪用者履行的,情形就比如你拜托了這個小伙子送花給N個MM,而又想准時地給這N個MM打德律風的情形一樣。
using System;
using System.Threading;
public class TestMain
{
private static ManualResetEvent ent = new ManualResetEvent(false);
public static void Main()
{
Boy sender = new Boy(ent);
Thread th = new Thread(new ThreadStart(sender.SendFlower));
th.Start();
ent.WaitOne(); //期待任務
Console.WriteLine("收到了吧,花是我送嘀:)");
Console.ReadLine();
}
}
public class Boy
{
ManualResetEvent ent;
public Boy(ManualResetEvent e)
{
ent = e;
}
public void SendFlower()
{
Console.WriteLine("正在送花的途中");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
Console.Write("..");
}
Console.WriteLine(" 花曾經送到MM手中了,boss");
ent.Set(); //告訴壅塞法式
}
}
而AutoResetEvent類故名思意,就是在每次Set完以後主動Reset。讓履行法式從新進入壅塞狀況。
即AutoResetEvent.Set() 相當於 ManualResetEvent.Set() 以後又立刻 ManualResetEvent.Reset(),其他的就沒有甚麼分歧的了。
舉個送花給N個MM的例子:
using System;
using System.Threading;
public class TestMain
{
private static AutoResetEvent ent = new AutoResetEvent(false);
public static void Main()
{
Boy sender = new Boy(ent);
for (int i = 0; i < 3; i++)
{
Thread th = new Thread(new ThreadStart(sender.SendFlower));
th.Start();
ent.WaitOne(); //期待任務
Console.WriteLine("收到了吧,花是我送嘀:) ");
}
Console.ReadLine();
}
}
public class Boy
{
AutoResetEvent ent;
public Boy(AutoResetEvent e)
{
ent = e;
}
public void SendFlower()
{
Console.WriteLine("正在送花的途中");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
Console.Write("..");
}
Console.WriteLine(" 花曾經送到MM手中了,boss");
ent.Set(); //告訴壅塞法式,這裡的後果相當於 ManualResetEvent的Set()辦法+Reset()辦法
}
}
願望本文所述對年夜家的C#法式設計有所贊助。