c#多線程的運用周全解析。本站提示廣大學習愛好者:(c#多線程的運用周全解析)文章只能為提供參考,不一定能成為您想要的結果。以下是c#多線程的運用周全解析正文
1.應用多線程的幾種方法
(1)不須要傳遞參數,也不須要前往參數
ThreadStart是一個拜托,這個拜托的界說為void ThreadStart(),沒有參數與前往值。
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 30; i++)
{
ThreadStart threadStart = new ThreadStart(Calculate);
Thread thread = new Thread(threadStart);
thread.Start();
}
Thread.Sleep(2000);
Console.Read();
}
public static void Calculate()
{
DateTime time = DateTime.Now;//獲得以後時光
Random ra = new Random();//隨機數對象
Thread.Sleep(ra.Next(10,100));//隨機休眠一段時光
Console.WriteLine(time.Minute + ":" + time.Millisecond);
}
}
(2)須要傳遞單個參數
ParameterThreadStart拜托界說為void ParameterizedThreadStart(object state),有一個參數然則沒有前往值。
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 30; i++)
{
ParameterizedThreadStart tStart = new ParameterizedThreadStart(Calculate);
Thread thread = new Thread(tStart);
thread.Start(i*10+10);//傳遞參數
}
Thread.Sleep(2000);
Console.Read();
}
public static void Calculate(object arg)
{
Random ra = new Random();//隨機數對象
Thread.Sleep(ra.Next(10, 100));//隨機休眠一段時光
Console.WriteLine(arg);
}
}
(3)應用專門的線程類(經常使用)
應用線程類可以有多個參數與多個前往值,非常靈巧!
class Program
{
static void Main(string[] args)
{
MyThread mt = new MyThread(100);
ThreadStart threadStart = new ThreadStart(mt.Calculate);
Thread thread = new Thread(threadStart);
thread.Start();
//期待線程停止
while (thread.ThreadState != ThreadState.Stopped)
{
Thread.Sleep(10);
}
Console.WriteLine(mt.Result);//打印前往值
Console.Read();
}
}
public class MyThread//線程類
{
public int Parame { set; get; }//參數
public int Result { set; get; }//前往值
//結構函數
public MyThread(int parame)
{
this.Parame = parame;
}
//線程履行辦法
public void Calculate()
{
Random ra = new Random();//隨機數對象
Thread.Sleep(ra.Next(10, 100));//隨機休眠一段時光
Console.WriteLine(this.Parame);
this.Result = this.Parame * ra.Next(10, 100);
}
}
(4)應用匿名辦法(經常使用)
應用匿名辦法啟動線程可以有多個參數和前往值,並且應用異常便利!
class Program
{
static void Main(string[] args)
{
int Parame = 100;//當作參數
int Result = 0;//當作前往值
//匿名辦法
ThreadStart threadStart = new ThreadStart(delegate()
{
Random ra = new Random();//隨機數對象
Thread.Sleep(ra.Next(10, 100));//隨機休眠一段時光
Console.WriteLine(Parame);//輸入參數
Result = Parame * ra.Next(10, 100);//盤算前往值
});
Thread thread = new Thread(threadStart);
thread.Start();//多線程啟動匿名辦法
//期待線程停止
while (thread.ThreadState != ThreadState.Stopped)
{
Thread.Sleep(10);
}
Console.WriteLine(Result);//打印前往值
Console.Read();
}
}
(5)應用拜托開啟多線程(多線程深刻)
1、用拜托(Delegate)的BeginInvoke和EndInvoke辦法操作線程
BeginInvoke辦法可使用線程異步地履行拜托所指向的辦法。然後經由過程EndInvoke辦法取得辦法的前往值(EndInvoke辦法的前往值就是被挪用辦法的前往值),或是肯定辦法曾經被勝利挪用。
class Program
{
private delegate int NewTaskDelegate(int ms);
private static int newTask(int ms)
{
Console.WriteLine("義務開端");
Thread.Sleep(ms);
Random random = new Random();
int n = random.Next(10000);
Console.WriteLine("義務完成");
return n;
}
static void Main(string[] args)
{
NewTaskDelegate task = newTask;
IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
//EndInvoke辦法將被壅塞2秒
int result = task.EndInvoke(asyncResult);
Console.WriteLine(result);
Console.Read();
}
}
2、應用IAsyncResult.IsCompleted屬性來斷定異步驟用能否完成
class Program
{
private delegate int NewTaskDelegate(int ms);
private static int newTask(int ms)
{
Console.WriteLine("義務開端");
Thread.Sleep(ms);
Random random = new Random();
int n = random.Next(10000);
Console.WriteLine("義務完成");
return n;
}
static void Main(string[] args)
{
NewTaskDelegate task = newTask;
IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
//期待異步履行完成
while (!asyncResult.IsCompleted)
{
Console.Write("*");
Thread.Sleep(100);
}
// 因為異步驟用曾經完成,是以, EndInvoke會連忙前往成果
int result = task.EndInvoke(asyncResult);
Console.WriteLine(result);
Console.Read();
}
}
3、應用WaitOne辦法期待異步辦法履行完成
WaitOne的第一個參數表現要期待的毫秒數,在指准時間以內,WaitOne辦法將一向期待,直到異步驟用完成,並收回告訴,WaitOne辦法才前往true。當期待指准時間以後,異步驟用仍未完成,WaitOne辦法前往false,假如指准時間為0,表現不期待,假如為-1,表現永久期待,直到異步驟用完成。
class Program
{
private delegate int NewTaskDelegate(int ms);
private static int newTask(int ms)
{
Console.WriteLine("義務開端");
Thread.Sleep(ms);
Random random = new Random();
int n = random.Next(10000);
Console.WriteLine("義務完成");
return n;
}
static void Main(string[] args)
{
NewTaskDelegate task = newTask;
IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
//期待異步履行完成
while (!asyncResult.AsyncWaitHandle.WaitOne(100, false))
{
Console.Write("*");
}
int result = task.EndInvoke(asyncResult);
Console.WriteLine(result);
Console.Read();
}
}
4、應用回調方法前往成果
要留意的是“my.BeginInvoke(3,300, MethodCompleted, my)”,BeginInvoke辦法的參數傳遞方法:
後面一部門(3,300)是其拜托自己的參數。
倒數第二個參數(MethodCompleted)是回調辦法拜托類型,他是回調辦法的拜托,此拜托沒有前往值,有一個IAsyncResult類型的參數,當method辦法履行完後,體系會主動挪用MethodCompleted辦法。
最初一個參數(my)須要向MethodCompleted辦法中傳遞一些值,普通可以傳遞被挪用辦法的拜托,這個值可使用IAsyncResult.AsyncState屬性取得。
class Program
{
private delegate int MyMethod(int second, int millisecond);
//線程履行辦法
private static int method(int second, int millisecond)
{
Console.WriteLine("線程休眠" + (second * 1000 + millisecond) + "毫秒");
Thread.Sleep(second * 1000 + millisecond);
Random random = new Random();
return random.Next(10000);
}
//回調辦法
private static void MethodCompleted(IAsyncResult asyncResult)
{
if (asyncResult == null || asyncResult.AsyncState == null)
{
Console.WriteLine("回調掉敗!!!");
return;
}
int result = (asyncResult.AsyncState as MyMethod).EndInvoke(asyncResult);
Console.WriteLine("義務完成,成果:" + result);
}
static void Main(string[] args)
{
MyMethod my = method;
IAsyncResult asyncResult = my.BeginInvoke(3,300, MethodCompleted, my);
Console.WriteLine("義務開端");
Console.Read();
}
}
5、其他組件的BeginXXX和EndXXX辦法
在其他的.net組件中也有相似BeginInvoke和EndInvoke的辦法,如System.Net.HttpWebRequest類的BeginGetResponse和EndGetResponse辦法。其應用辦法相似於拜托類型的BeginInvoke和EndInvoke辦法,例如:
class Program
{
//回調函數
private static void requestCompleted(IAsyncResult asyncResult)
{
if (asyncResult == null || asyncResult.AsyncState==null)
{
Console.WriteLine("回調掉敗");
return;
}
HttpWebRequest hwr = asyncResult.AsyncState as HttpWebRequest;
HttpWebResponse response = (HttpWebResponse)hwr.EndGetResponse(asyncResult);
StreamReader sr = new StreamReader(response.GetResponseStream());
string str = sr.ReadToEnd();
Console.WriteLine("前往流長度:"+str.Length);
}
static void Main(string[] args)
{
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create("http://www.百度.com");
//異步要求
IAsyncResult asyncResult = request.BeginGetResponse(requestCompleted, request);
Console.WriteLine("義務開端");
Console.Read();
}
}
2.線程的狀況掌握
(1)前台線程與後台線程
Thread.IsBackground屬性為true則是後台線程,為false則是前台線程。後台線程與前台線程差別以下:
a.當在主線程中創立了一個線程,那末該線程的IsBackground默許是設置為false的。
b.當主線程加入的時刻,IsBackground=false的線程還會持續履行下去,直到線程履行停止。只要IsBackground=true的線程才會跟著主線程的加入而加入。
c.現在始化一個線程,把Thread.IsBackground=true的時刻,指導該線程為後台線程。後台線程將會跟著主線程的加入而加入。
d.道理:只需一切前台線程都終止後,CLR就會對每個活在的後台線程挪用Abort()來完全終止運用法式
(2)由線程類(Thread)啟動動的線程狀況掌握
應用System.Threading.ThreadState與System.Diagnostics.ThreadState列舉斷定線程狀況,與Thread.ThreadState 屬性合營應用。
System.Threading.ThreadState列舉狀況:
System.Diagnostics.ThreadState列舉狀況:
留意:您的代碼在任何情形下都不該應用線程狀況來同步線程的運動。
(3)由拜托啟動的線程的狀況掌握
拜托的EndInvoke辦法阻攔以後線程運轉,直到拜托異步履行完成。
IAsyncResult.IsCompleted屬性表現拜托異步履行能否完成。
拜托的WaitOne辦法期待異步辦法履行完成。
3.多線程拜訪GUI界面的處置
(1)多線程在GUI編程時湧現的成績
在GUI編程時,假如你從非創立這個控件的線程中拜訪這個控件或許操作這個控件的話就會拋出這個異常。這是微軟為了包管線程平安和進步代碼的效力所做的改良,然則也給年夜家帶來許多未便。
(2)經由過程設置處置
設置System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;在你的法式初始化的時刻設置了這個屬性,並且在你的控件中應用的都是微軟Framework類庫中的控件的話,體系就不會再拋出你下面所說的這個毛病了。
(3)經由過程拜托處置(建議應用)
//按鈕事宜
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(Flush);
thread.IsBackground = true;//設置成後台線程
thread.Start();
}
//線程履行的辦法
private void Flush()
{
//界說拜托
Action action = delegate()
{
this.textBox1.AppendText(DateTime.Now.ToString() + "\r\n");
};
while (true)
{
//斷定可否到以後線程操作該組件
if (this.textBox1.InvokeRequired)
{
//不在以後線程上操作
this.textBox1.Invoke(action);//挪用拜托
}
else
{
//在以後線程上操作
this.textBox1.AppendText(DateTime.Now.ToString() + "\r\n");
}
Thread.Sleep(1000);
}
留意:應用該方法不會有沒有呼應的情形產生,激烈建議應用該方法。此方法不會產生界面無呼應的症結點:挪用this.textBox1.Invoke(action)就是在具有this.textBox1對象的線程(紛歧定是以後線程,多半是主線程)上挪用action拜托,若action拜托指向的辦法履行時光太長就會使得界面無呼應!而該方法中action拜托指向的辦法履行時光極其短。
(4)挪用控件的Invoke和BeginInvoke辦法的差別
在多線程編程中,我們常常要在任務線程中去更新界面顯示,而在多線程中直接挪用界面控件的辦法是毛病的做法,准確的做法是將任務線程中觸及更新界面的代碼封裝為一個辦法,經由過程Invoke或許BeginInvoke去挪用,二者的差別就是Invoke招致任務線程期待,而BeginInvoke則不會。
而所謂的“一面呼應操作,一面添加節點”永久只能是絕對的,使UI線程的累贅不至於太年夜而以,由於界面的准確更新一直要經由過程UI線程去做,我們要做的工作是在任務線程中包辦年夜部門的運算,而將對純潔的界面更新放到UI線程中去做,如許也就到達了加重UI線程累贅的目標了。
(5)Application.DoEvents()挪用新聞處置法式
在耗時的輪回的UI更新的辦法中,拔出Application.DoEvents(),會使界面取得呼應,Application.DoEvents()會挪用新聞處置法式。
(6)應用BackgroundWorker組件
重要的事宜及參數:
1.DoWork—當履行BackgroundWorker.RunWorkerAsync辦法時會觸發該事宜,而且傳遞DoWorkEventArgs參數;
2.ProgressChanged—操作處置中取得的處置狀況變更,經由過程BackgroundWorker.ReportProgress辦法觸發該事宜,而且傳遞ProgressChangedEventArgs,個中包括了處置的百分比,這個參數在UI界面上設置progressbar控件。
3.RunWorkerCompleted—異步操作完成或半途終止會觸發該事宜。假如須要提早終止履行後台操作,可以挪用BackgroundWorker.CancelAsync辦法。在處置DoWork事宜的函數中檢測BackgroundWorker.CancellationPending屬性能否為true,假如是true,則表現用戶曾經撤消了異步驟用,同時將DoWorkEventArgs.Cancel屬性設為true(傳遞給處置DoWork事宜的函數的第二個參數),如許當加入異步驟用的時刻,可讓處置RunWorkerCompleted事宜的函數曉得是正常加入照樣半途加入。
重要的辦法:
1. BackgroundWorker.RunWorkerAsync—“起動”異步驟用的辦法有兩次重載RunWorkerAsync(),RunWorkerAsync(object argument),第二個重載供給了一個參數,可以供異步驟用應用。(假如有多個參數要傳遞怎樣辦,應用一個類來傳遞他們吧)。挪用該辦法後會觸發DoWork事宜,而且為處置DoWork事宜的函數傳遞DoWorkEventArg參數,個中包括了RunWorkerAsync傳遞的參數。在響應DoWork的處置函數中便可以做詳細的龐雜操作。
2. BackgroundWorker.ReportProgress—須要在一個冗雜的操作中向用戶赓續反應進度,如許的話便可以挪用的ReportProgress(int percent),在挪用 ReportProgress 辦法時,觸發ProgressChanged事宜。供給一個在 0 到 100 之間的整數,它表現後台運動已完成的百分比。你也能夠供給任何對象作為第二個參數,許可你給事宜處置法式傳遞狀況信息。作為傳遞到此進程的 ProgressChangedEventArgs 參數屬性,百分比和你本身的對象(假如供給的話)均要被傳遞到 ProgressChanged 事宜處置法式。這些屬性被分離定名為 ProgressPercentage 和 UserState,而且你的事宜處置法式可以以任何須要的方法應用它們。(留意:只要在BackgroundWorker.WorkerReportsProgress屬性被設置為true該辦法才可用)。
3. BackgroundWorker.CancelAsync—但須要加入異步驟用的時刻,就挪用的這個辦法。然則樣還不敷,由於它僅僅是將BackgroudWorker.CancellationPending屬性設置為true。你須要在詳細的異步驟用途理的時刻,赓續檢討BackgroudWorker.CancellationPending能否為true,假如是真的話就加入。(留意:只要在BackgroundWorker.WorkerSupportsCancellation屬性被設置為true該辦法才可用)。
//按鈕事宜
private void button1_Click(object sender, EventArgs e)
{
this.backgroundWorker1.RunWorkerAsync();//處置事務
}
//處置事務事宜
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//初始化進度條
this.progressBar1.Maximum = 100;
this.progressBar1.Minimum = 0;
//模仿事物處置
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
//部分操作完成事宜觸發
this.backgroundWorker1.ReportProgress(i, null);
}
}
//部分操作完成時履行的辦法
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;//設置進度條值
}
//事物處置完成時觸發
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
MessageBox.Show(null, "任務線程完成!", "提醒");
}
4.線程池
(1)線程池的感化
很多時刻,我們須要用多線程,然則又不願望線程的數目過量,這就是線程池的感化,.Net為我們供給了現成的線程池ThreadPool。
(2)線程池的應用
class Program
{
//線程辦法
public static void ThreadProc(object i)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
public static void Main()
{
ThreadPool.SetMaxThreads(3, 3);//設置線程池
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), "線程" + i);
}
Console.WriteLine("運轉停止");
Console.Read();
}
}
每個過程都有一個線程池,線程池的默許年夜小是25,我們可以經由過程SetMaxThreads辦法來設置其最年夜值。
留意:由於WaitCallback拜托的原型是void WaitCallback(object state),那沒有方法,我們只能將多個參數封裝到一個Object中。
5.線程同步
(1)代碼塊同步(Monitor與lock )
1、解釋:
應用Monitor類的應用與lock症結字的應用在完成道理上雷同。
Monitor.Enter 辦法:在指定對象上獲得排他鎖。
Monitor.TryEnter 辦法:試圖獲得指定對象的排他鎖。
Monitor.Exit 辦法:釋放指定對象上的排他鎖。
Monitor.Wait 辦法:釋放對象上的鎖並壅塞以後線程,直到它從新獲得該鎖。
Monitor.Pulse 辦法:告訴期待隊列中的線程鎖定對象狀況的更改。
Monitor.PulseAll 辦法:告訴一切的期待線程對象狀況的更改。
2、示例
class Program
{
private int Count = 0;
//線程履行辦法
public void ThreadProc()
{
Monitor.Enter(this);
Thread.Sleep(200);
Count++;
Console.WriteLine(Count);
Monitor.Exit(this);
//同等於
//lock (this)
//{
// Thread.Sleep(200);
// Count++;
// Console.WriteLine(Count);
//}
}
public static void Main()
{
Program p = new Program();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(p.ThreadProc);
t.Start();
}
Console.Read();
}
}
(2)WaitHandle引見
WaitHandle是一個籠統類,上面是從它繼續來的幾個類:
Mutex:一個同步基元,也可用於過程間同步。
AutoResetEvent:告訴正在期待的線程已產生事宜。沒法繼續此類。
ManualResetEvent:告訴一個或多個正在期待的線程已產生事宜。沒法繼續此類。
WaitHandle的幾個辦法:
WaitAll:期待指定命組中的一切元素收到旌旗燈號。
WaitAny:期待指定命組中的任一元素收到旌旗燈號。
WaitOne:當在派生類中重寫時,壅塞以後線程,直到以後的WaitHandle收到旌旗燈號。
(3)應用Mutex
1、應用Mutex掌握線程同步
class Program
{
private static Mutex mutex;
static void Main(string[] args)
{
mutex = new Mutex(false);
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(Method);
thread.Start("線程" + i);
}
Console.WriteLine("主線程履行終了");
Console.ReadLine();
}
//線程履行辦法
private static void Method(Object o)
{
mutex.WaitOne();//期待旌旗燈號
for (int i = 0; i < 3; i++)
{
Thread.Sleep(500);
Console.WriteLine(o.ToString() + "輪回" + i);
}
mutex.ReleaseMutex();//釋放旌旗燈號
}
}
留意:關於WaitAll、WaitAny、WaitOne辦法的應用請參考AutoResetEvent與ManualResetEvent。
2、應用Mutex掌握過程間的同步
class Program
{
static void Main(string[] args)
{
bool flag = false;
Mutex mutex = new Mutex(true, "Test", out flag);
//第一個參數:true--給挪用線程付與互斥體的初始所屬權
//第一個參數:互斥體的稱號
//第三個參數:前往值,假如挪用線程已被授與互斥體的初始所屬權,則前往true
if (flag)
{
Console.Write("過程運轉...");
}
else
{
Console.Write("這個過程正在運轉!");
Thread.Sleep(5000);//線程掛起5秒鐘
Environment.Exit(1);//加入法式
}
Console.ReadLine();
}
}
運轉以上代碼生成的運用法式第一個實例,會獲得成果:過程運轉...
堅持第一個運轉狀況,運轉第二個實例,獲得成果:這個過程正在運轉!
留意:以上代碼中創立了一個mutex,從其參數的說明中得知,第一個挪用線程將獲得互斥體的初始所屬權,假如不釋放的話,其他的線程得不到互斥體一切權
(4)應用AutoResetEvent
1、應用WaitAll靜態辦法
懂得AutoResetEvent.WaitAll(Waits)靜態辦法:WaitAll靜態辦法就是壅塞以後線程,直到Waits數組裡的一切元素都挪用Set()辦法發送旌旗燈號,再持續履行以後線程。
class Program
{
public static void Main()
{
AutoResetEvent[] Waits = new AutoResetEvent[10];
for (int i = 0; i < 10; i++)
{
int temp = i;
Waits[temp] = new AutoResetEvent(false);
Action thread = delegate()
{
//線程履行辦法
Console.WriteLine("線程:" + temp);
Thread.Sleep(1000);
Waits[temp].Set();//發送線程履行終了旌旗燈號
};
ThreadStart ts = new ThreadStart(thread);
Thread t = new Thread(ts);
t.Start();
}
AutoResetEvent.WaitAll(Waits);//期待Waits中的一切對象收回旌旗燈號
Console.WriteLine("線程全體履行終了!");
Console.Read();
}
}
2、應用WaitAny靜態辦法
懂得AutoResetEvent.WaitAny(Waits)靜態辦法:WaitAny靜態辦法就是壅塞以後線程,只需Waits數組有一個元素挪用Set()辦法發送旌旗燈號,就持續履行以後線程。
class Program
{
public static void Main()
{
AutoResetEvent[] Waits = new AutoResetEvent[10];
for (int i = 0; i < 10; i++)
{
Waits[i] = new AutoResetEvent(false);//初始化Waits
}
for (int i = 0; i < 10; i++)
{
int temp = i;
Action thread = delegate()
{
if (temp > 0)
{
AutoResetEvent.WaitAny(Waits);//期待上一個線程履行終了
}
//線程履行辦法
Thread.Sleep(1000);
Waits[temp].Set();//發送線程履行終了旌旗燈號
Console.WriteLine("線程:" + temp+"履行終了");
};
ThreadStart ts = new ThreadStart(thread);
Thread t = new Thread(ts);
t.Start();
}
Console.Read();
}
}
3、應用WaitOne成員辦法
懂得Wait.WaitOne()成員辦法:WaitOne辦法就是壅塞以後線程,只需Wait對象挪用了Set()辦法發送旌旗燈號,就持續履行以後線程。
class Program
{
public static void Main()
{
AutoResetEvent Wait = new AutoResetEvent(false);
for (int i = 0; i < 10; i++)
{
Action thread = delegate()
{
//線程履行辦法
Thread.Sleep(1000);
Wait.Set();//發送線程履行終了旌旗燈號
Console.WriteLine("線程:" + i+"履行終了");
};
ThreadStart ts = new ThreadStart(thread);
Thread t = new Thread(ts);
t.Start();
Wait.WaitOne();//期待挪用 Waits.Set()
}
Console.Read();
}
}
(5)應用ManualResetEvent與AutoResetEvent的差別
1、ManualResetEvent與AutoResetEvent的雷同點
關於WaitAll、WaitAny、WaitOne辦法的應用ManualResetEvent與AutoResetEvent對象是沒有差別的。
2、ManualResetEvent與AutoResetEvent的差別
然則關於Set()辦法AutoResetEvent只會給一個線程發送旌旗燈號,而ManualResetEvent會給多個線程發送旌旗燈號。在我們須要同步多個線程的時刻,就只能采取ManualResetEvent了。至於深條理的緣由是,AutoResetEvent在Set()以後,會將線程狀況主動置為false,而ManualResetEvent在Set()後,線程的狀況就變成true了,必需手動ReSet()以後,才會從新將線程置為false。這也就是為何他們的名字一個為Auto(主動),一個為Manual(手動)的緣由。
class Program
{
private static ManualResetEvent Wait = new ManualResetEvent(false);
public static void Main()
{
Wait.Set();//設置線程狀況為許可履行
Thread thread1 = new Thread(Method);
thread1.Start("線程1");
Thread.Sleep(1000);//期待線程1履行
Wait.Reset();//必需手動復位線程狀況,使狀況為不許可履行
Thread thread2 = new Thread(Method);
thread2.Start("線程2");//線程2將會一向期待旌旗燈號
Console.WriteLine("主線程停止");
Console.Read();
}
//線程履行辦法
private static void Method(Object o)
{
Wait.WaitOne();//期待旌旗燈號
Console.WriteLine(o.ToString());
}
}
(6)應用Interlocked停止原子操作
Interlocked類為多個線程同享的變量供給原子操作。
原子操作:Interlocked.Increment()操作是一個原子操作,感化是:Count++ 。原子操作,就是不克不及被更高級級中止掠奪優先的操作。因為操作體系年夜部門時光處於開中止狀況,所以,一個法式在履行的時刻能夠被優先級更高的線程中止。而有些操作是不克不及被中止的,否則會湧現沒法復原的效果,這時候候,這些操作就須要原子操作。就是不克不及被中止的操作。
class Program
{
private static int Count = 0;
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
Thread thread = new Thread(Method);
thread.Start("線程" + i);
}
Thread.Sleep(1000 * 3);//休眠足夠的時光期待一切線程履行終了
Console.WriteLine("操作後的成果:" + Program.Count);
Console.ReadLine();
}
//線程履行辦法
private static void Method(Object o)
{
Thread.Sleep(500);
//原子操作,相似:Program.Count++
Interlocked.Increment(ref Program.Count);
//Program.Count++;//非原子操作
Console.WriteLine(o.ToString());
}
}
(7)應用ReaderWriterLock
應用Monitor或Mutex停止同步掌握的成績:因為獨有拜訪模子不許可任何情勢的並發拜訪,如許的效力老是不太高。很多時刻,運用法式在拜訪資本時是停止讀操作,寫操作絕對較少。為處理這一成績,C#供給了System.Threading.ReaderWriterLock類以順應多用戶讀/單用戶寫的場景。該類可完成以下功效:假如資本未被寫操作鎖定,那末任何線程都可對該資本停止讀操作鎖定,而且對讀操作鎖數目沒無限制,即多個線程可同時對該資本停止讀操作鎖定,以讀取數據。假如資本未被添加任何讀或寫操作鎖,那末一個且唯一一個線程可對該資本添加寫操作鎖定,以寫入數據。簡略的講就是:讀操作鎖是同享鎖,許可多個線程同時讀取數據;寫操作鎖是獨有鎖,統一時辰,僅許可一個線程停止寫操作。
ReaderWriterLock類:界說支撐單個寫線程和多個讀線程的鎖。
ReaderWriterLockSlim類:表現用於治理資本拜訪的鎖定狀況,可完成多線程讀取或停止獨有式寫入拜訪。
class Program
{
private static int Count = 0;//資本
static ReaderWriterLock rwl = new ReaderWriterLock();//讀、寫操作鎖
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(Read);//讀線程
thread.Start("線程" + i);
}
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(Write);//寫線程
thread.Start("--線程" + i);
}
Console.ReadKey();
}
private static void Read(Object o)//讀數據
{
rwl.AcquireReaderLock(1000 * 20); //請求讀操作鎖,在20s內未獲得讀操作鎖,則廢棄
Console.WriteLine(o.ToString() + "讀取數據:" + Program.Count);
Thread.Sleep(500);
rwl.ReleaseReaderLock();//釋放讀操作鎖
}
private static void Write(Object o)//寫數據
{
rwl.AcquireWriterLock(1000 * 20);//請求寫操作鎖,在20s內未獲得寫操作鎖,則廢棄
Thread.Sleep(500);
Console.WriteLine(o.ToString() + "寫數據:" + (++Program.Count));
rwl.ReleaseWriterLock();//釋放寫操作鎖
}
}
(8)應用Semaphore
Semaphore類:限制可同時拜訪某一資本或資本池的線程數。
class Program
{
private static Semaphore semaphore = new Semaphore(0, 5);//初始化旌旗燈號量
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(Method);
thread.Start("線程" + i);
}
semaphore.Release(2);//釋放旌旗燈號量2個
Console.WriteLine("主線程運轉終了!");
Console.Read();
}
//線程履行辦法
private static void Method(object o)
{
semaphore.WaitOne();//期待旌旗燈號量
Thread.Sleep(1000);
Console.WriteLine(o.ToString());
semaphore.Release();//釋放旌旗燈號量
}
}
其它的線程只要比及主線程釋放才會履行,由於我給旌旗燈號量計數器的初始值是0,所以其它線程在主線程釋放前都邑被壅塞。爾後,我在主線程直接用Release(2)函數將計數器置為2,所以2個線程可以同時獲得履行。
留意:可以給旌旗燈號量設置一個稱號,這個稱號是操作體系可見的,是以,可使用這些旌旗燈號量來調和跨過程界限的資本應用。
class Program
{
static void Main(string[] args)
{
//初始旌旗燈號量5個,最多旌旗燈號量10個
Semaphore seamphore = new Semaphore(5, 10, "Test");
seamphore.WaitOne();//期待旌旗燈號
Console.WriteLine("獲得旌旗燈號量 1");
seamphore.WaitOne();//期待旌旗燈號
Console.WriteLine("獲得旌旗燈號量 2");
seamphore.WaitOne();//期待旌旗燈號
Console.WriteLine("獲得旌旗燈號量 3");
Console.WriteLine("主線程運轉終了!");
Console.Read();
}
}
運轉兩個如許的法式,成果以下,在第二個運轉的示例中,會將壅塞在第三個旌旗燈號量上:
6.准時器Timer
(1)經常使用的3個Timer類
System.Threading.Timer 供給以指定的時光距離履行辦法的機制。沒法繼續此類。
System.Timers.Timer 在運用法式中生成按期事宜。
System.Windows.Forms.Timer 完成按用戶界說的時光距離激發事宜的計時器。此計時器最宜用於Windows窗體運用法式中,而且必需在窗口中應用。
(2)System.Timers.Timer的應用示例
class Program
{
static void Main(string[] args)
{
System.Timers.Timer t = new System.Timers.Timer(1000);//發生事宜的時光距離1s
t.Elapsed += new ElapsedEventHandler(Method); //達到時光的時刻履行事宜
t.AutoReset = true;//設置是履行一次(false)照樣一向履行(true)
t.Enabled = true;//能否履行System.Timers.Timer.Elapsed事宜
Console.WriteLine("完成!");
Console.Read();
}
private static void Method(object source,ElapsedEventArgs e)
{
Console.WriteLine("時光:"+e.SignalTime);
}
}