欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c#多線程的應(yīng)用全面解析

 更新時(shí)間:2014年01月26日 15:52:56   作者:  
這篇文章主要介紹了c#多線程的應(yīng)用,有需要的朋友可以參考一下

1.使用多線程的幾種方式

(1)不需要傳遞參數(shù),也不需要返回參數(shù)

ThreadStart是一個(gè)委托,這個(gè)委托的定義為void ThreadStart(),沒(méi)有參數(shù)與返回值。

復(fù)制代碼 代碼如下:

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;//得到當(dāng)前時(shí)間

            Random ra = new Random();//隨機(jī)數(shù)對(duì)象

            Thread.Sleep(ra.Next(10,100));//隨機(jī)休眠一段時(shí)間

            Console.WriteLine(time.Minute + ":" + time.Millisecond);

        }

}


(2)需要傳遞單個(gè)參數(shù)
復(fù)制代碼 代碼如下:

ParameterThreadStart委托定義為void ParameterizedThreadStart(object state),有一個(gè)參數(shù)但是沒(méi)有返回值。

    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);//傳遞參數(shù)

            }

            Thread.Sleep(2000);

            Console.Read();

        }

        public static void Calculate(object arg)

        {

            Random ra = new Random();//隨機(jī)數(shù)對(duì)象

            Thread.Sleep(ra.Next(10, 100));//隨機(jī)休眠一段時(shí)間

            Console.WriteLine(arg);

        }

}


(3)使用專門的線程類(常用)

使用線程類可以有多個(gè)參數(shù)與多個(gè)返回值,十分靈活!

復(fù)制代碼 代碼如下:

    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();

   //等待線程結(jié)束

            while (thread.ThreadState != ThreadState.Stopped)

            {

                Thread.Sleep(10);

            }

            Console.WriteLine(mt.Result);//打印返回值

            Console.Read();

        }

    }

    public class MyThread//線程類

    {

        public int Parame { set; get; }//參數(shù)

        public int Result { set; get; }//返回值

        //構(gòu)造函數(shù)

        public MyThread(int parame)

        {

            this.Parame = parame;

        }

        //線程執(zhí)行方法

        public void Calculate()

        {

            Random ra = new Random();//隨機(jī)數(shù)對(duì)象

            Thread.Sleep(ra.Next(10, 100));//隨機(jī)休眠一段時(shí)間

            Console.WriteLine(this.Parame);

            this.Result = this.Parame * ra.Next(10, 100);

        }

}


(4)使用匿名方法(常用)

使用匿名方法啟動(dòng)線程可以有多個(gè)參數(shù)和返回值,而且使用非常方便!

復(fù)制代碼 代碼如下:

    class Program

    {

        static void Main(string[] args)

        {

            int Parame = 100;//當(dāng)做參數(shù)

            int Result = 0;//當(dāng)做返回值

            //匿名方法

            ThreadStart threadStart = new ThreadStart(delegate()

            {

                Random ra = new Random();//隨機(jī)數(shù)對(duì)象

                Thread.Sleep(ra.Next(10, 100));//隨機(jī)休眠一段時(shí)間

                Console.WriteLine(Parame);//輸出參數(shù)

                Result = Parame * ra.Next(10, 100);//計(jì)算返回值

            });

            Thread thread = new Thread(threadStart);

            thread.Start();//多線程啟動(dòng)匿名方法

            //等待線程結(jié)束

            while (thread.ThreadState != ThreadState.Stopped)

            {

                Thread.Sleep(10);

            }

            Console.WriteLine(Result);//打印返回值

            Console.Read();

        }

    }


(5)使用委托開啟多線程(多線程深入)

1、用委托(Delegate)的BeginInvoke和EndInvoke方法操作線程

復(fù)制代碼 代碼如下:

BeginInvoke方法可以使用線程異步地執(zhí)行委托所指向的方法。然后通過(guò)EndInvoke方法獲得方法的返回值(EndInvoke方法的返回值就是被調(diào)用方法的返回值),或是確定方法已經(jīng)被成功調(diào)用。

    class Program

    {

        private delegate int NewTaskDelegate(int ms);

        private static int newTask(int ms)

        {

            Console.WriteLine("任務(wù)開始");

            Thread.Sleep(ms);

            Random random = new Random();

            int n = random.Next(10000);

            Console.WriteLine("任務(wù)完成");

            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屬性來(lái)判斷異步調(diào)用是否完成
復(fù)制代碼 代碼如下:

    class Program

    {

        private delegate int NewTaskDelegate(int ms);

        private static int newTask(int ms)

        {

            Console.WriteLine("任務(wù)開始");

            Thread.Sleep(ms);

            Random random = new Random();

            int n = random.Next(10000);

            Console.WriteLine("任務(wù)完成");

            return n;

        }

        static void Main(string[] args)

        {

            NewTaskDelegate task = newTask;

            IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);

            //等待異步執(zhí)行完成

            while (!asyncResult.IsCompleted)

            {

                Console.Write("*");

                Thread.Sleep(100);

            }

            // 由于異步調(diào)用已經(jīng)完成,因此, EndInvoke會(huì)立刻返回結(jié)果

            int result = task.EndInvoke(asyncResult);

            Console.WriteLine(result);

            Console.Read();

        }

    }


3、使用WaitOne方法等待異步方法執(zhí)行完成

   WaitOne的第一個(gè)參數(shù)表示要等待的毫秒數(shù),在指定時(shí)間之內(nèi),WaitOne方法將一直等待,直到異步調(diào)用完成,并發(fā)出通知,WaitOne方法才返回true。當(dāng)?shù)却付〞r(shí)間之后,異步調(diào)用仍未完成,WaitOne方法返回false,如果指定時(shí)間為0,表示不等待,如果為-1,表示永遠(yuǎn)等待,直到異步調(diào)用完成。

復(fù)制代碼 代碼如下:

    class Program

    {

        private delegate int NewTaskDelegate(int ms);

        private static int newTask(int ms)

        {

            Console.WriteLine("任務(wù)開始");

            Thread.Sleep(ms);

            Random random = new Random();

            int n = random.Next(10000);

            Console.WriteLine("任務(wù)完成");

            return n;

        }

        static void Main(string[] args)

        {

            NewTaskDelegate task = newTask;

            IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);

            //等待異步執(zhí)行完成

            while (!asyncResult.AsyncWaitHandle.WaitOne(100, false))

            {

                Console.Write("*");

            }

            int result = task.EndInvoke(asyncResult);

            Console.WriteLine(result);

            Console.Read();

        }

    }


4、使用回調(diào)方式返回結(jié)果

要注意的是“my.BeginInvoke(3,300, MethodCompleted, my)”,BeginInvoke方法的參數(shù)傳遞方式:

前面一部分(3,300)是其委托本身的參數(shù)。

倒數(shù)第二個(gè)參數(shù)(MethodCompleted)是回調(diào)方法委托類型,他是回調(diào)方法的委托,此委托沒(méi)有返回值,有一個(gè)IAsyncResult類型的參數(shù),當(dāng)method方法執(zhí)行完后,系統(tǒng)會(huì)自動(dòng)調(diào)用MethodCompleted方法。

最后一個(gè)參數(shù)(my)需要向MethodCompleted方法中傳遞一些值,一般可以傳遞被調(diào)用方法的委托,這個(gè)值可以使用IAsyncResult.AsyncState屬性獲得。

復(fù)制代碼 代碼如下:

    class Program

    {

        private delegate int MyMethod(int second, int millisecond);

        //線程執(zhí)行方法

        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);

        }

        //回調(diào)方法

        private static void MethodCompleted(IAsyncResult asyncResult)

        {

            if (asyncResult == null || asyncResult.AsyncState == null)

            {

                Console.WriteLine("回調(diào)失敗?。?!");

                return;

            }

            int result = (asyncResult.AsyncState as MyMethod).EndInvoke(asyncResult);

            Console.WriteLine("任務(wù)完成,結(jié)果:" + result);

        }

        static void Main(string[] args)

        {

            MyMethod my = method;

            IAsyncResult asyncResult = my.BeginInvoke(3,300, MethodCompleted, my);

            Console.WriteLine("任務(wù)開始");

            Console.Read();

        }

    }


5、其他組件的BeginXXX和EndXXX方法

在其他的.net組件中也有類似BeginInvoke和EndInvoke的方法,如System.Net.HttpWebRequest類的BeginGetResponse和EndGetResponse方法。其使用方法類似于委托類型的BeginInvoke和EndInvoke方法,例如:

復(fù)制代碼 代碼如下:

    class Program

    {

        //回調(diào)函數(shù)

        private static void requestCompleted(IAsyncResult asyncResult)

        {

            if (asyncResult == null || asyncResult.AsyncState==null)

            {

                Console.WriteLine("回調(diào)失敗");

                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("返回流長(zhǎng)度:"+str.Length);

        }

        static void Main(string[] args)

        {

            HttpWebRequest request =

(HttpWebRequest)WebRequest.Create("http://www.baidu.com");

            //異步請(qǐng)求

            IAsyncResult asyncResult = request.BeginGetResponse(requestCompleted, request);

            Console.WriteLine("任務(wù)開始");

            Console.Read();

        }

    }


2.線程的狀態(tài)控制

(1)前臺(tái)線程與后臺(tái)線程

Thread.IsBackground屬性為true則是后臺(tái)線程,為false則是前臺(tái)線程。后臺(tái)線程與前臺(tái)線程區(qū)別如下:

a.當(dāng)在主線程中創(chuàng)建了一個(gè)線程,那么該線程的IsBackground默認(rèn)是設(shè)置為false的。

b.當(dāng)主線程退出的時(shí)候,IsBackground=false的線程還會(huì)繼續(xù)執(zhí)行下去,直到線程執(zhí)行結(jié)束。只有IsBackground=true的線程才會(huì)隨著主線程的退出而退出。

c.當(dāng)初始化一個(gè)線程,把Thread.IsBackground=true的時(shí)候,指示該線程為后臺(tái)線程。后臺(tái)線程將會(huì)隨著主線程的退出而退出。

d.原理:只要所有前臺(tái)線程都終止后,CLR就會(huì)對(duì)每一個(gè)活在的后臺(tái)線程調(diào)用Abort()來(lái)徹底終止應(yīng)用程序

(2)由線程類(Thread)啟動(dòng)動(dòng)的線程狀態(tài)控制

使用System.Threading.ThreadState與System.Diagnostics.ThreadState枚舉判斷線程狀態(tài),與Thread.ThreadState 屬性配合使用。

System.Threading.ThreadState枚舉狀態(tài):

System.Diagnostics.ThreadState枚舉狀態(tài):

注意:您的代碼在任何情況下都不應(yīng)使用線程狀態(tài)來(lái)同步線程的活動(dòng)。

(3)由委托啟動(dòng)的線程的狀態(tài)控制

委托的EndInvoke方法阻止當(dāng)前線程運(yùn)行,直到委托異步執(zhí)行完成。

IAsyncResult.IsCompleted屬性表示委托異步執(zhí)行是否完成。

委托的WaitOne方法等待異步方法執(zhí)行完成。

3.多線程訪問(wèn)GUI界面的處理

(1)多線程在GUI編程時(shí)出現(xiàn)的問(wèn)題

在GUI編程時(shí),如果你從非創(chuàng)建這個(gè)控件的線程中訪問(wèn)這個(gè)控件或者操作這個(gè)控件的話就會(huì)拋出這個(gè)異常。這是微軟為了保證線程安全以及提高代碼的效率所做的改進(jìn),但是也給大家?guī)?lái)很多不便。

(2)通過(guò)設(shè)置處理

設(shè)置System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;在你的程序初始化的時(shí)候設(shè)置了這個(gè)屬性,而且在你的控件中使用的都是微軟Framework類庫(kù)中的控件的話,系統(tǒng)就不會(huì)再拋出你上面所說(shuō)的這個(gè)錯(cuò)誤了。

(3)通過(guò)委托處理(建議使用)

復(fù)制代碼 代碼如下:

        //按鈕事件

        private void button1_Click(object sender, EventArgs e)

        {

            Thread thread = new Thread(Flush);

            thread.IsBackground = true;//設(shè)置成后臺(tái)線程

            thread.Start();

        }

        //線程執(zhí)行的方法

        private void Flush()

        {

            //定義委托

            Action action = delegate()

            {

                this.textBox1.AppendText(DateTime.Now.ToString() + "\r\n");

            };

            while (true)

            {

                //判斷能否到當(dāng)前線程操作該組件

                if (this.textBox1.InvokeRequired)

                {

                    //不在當(dāng)前線程上操作

                    this.textBox1.Invoke(action);//調(diào)用委托

                }

                else

                {

                    //在當(dāng)前線程上操作

                    this.textBox1.AppendText(DateTime.Now.ToString() + "\r\n");

                }

                Thread.Sleep(1000);

            }


注意:使用該方式不會(huì)有無(wú)響應(yīng)的情況發(fā)生,強(qiáng)烈建議使用該方式。此方式不會(huì)發(fā)生界面無(wú)響應(yīng)的關(guān)鍵點(diǎn):調(diào)用this.textBox1.Invoke(action)就是在擁有this.textBox1對(duì)象的線程(不一定是當(dāng)前線程,多數(shù)是主線程)上調(diào)用action委托,若action委托指向的方法執(zhí)行時(shí)間過(guò)長(zhǎng)就會(huì)使得界面無(wú)響應(yīng)!而該方式中action委托指向的方法執(zhí)行時(shí)間極為短。

(4)調(diào)用控件的Invoke和BeginInvoke方法的區(qū)別

在多線程編程中,我們經(jīng)常要在工作線程中去更新界面顯示,而在多線程中直接調(diào)用界面控件的方法是錯(cuò)誤的做法,正確的做法是將工作線程中涉及更新界面的代碼封裝為一個(gè)方法,通過(guò)Invoke或者BeginInvoke去調(diào)用,兩者的區(qū)別就是Invoke導(dǎo)致工作線程等待,而BeginInvoke則不會(huì)。

而所謂的“一面響應(yīng)操作,一面添加節(jié)點(diǎn)”永遠(yuǎn)只能是相對(duì)的,使UI線程的負(fù)擔(dān)不至于太大而以,因?yàn)榻缑娴恼_更新始終要通過(guò)UI線程去做,我們要做的事情是在工作線程中包攬大部分的運(yùn)算,而將對(duì)純粹的界面更新放到UI線程中去做,這樣也就達(dá)到了減輕UI線程負(fù)擔(dān)的目的了。

(5)Application.DoEvents()調(diào)用消息處理程序

在耗時(shí)的循環(huán)的UI更新的方法中,插入Application.DoEvents(),會(huì)使界面獲得響應(yīng),Application.DoEvents()會(huì)調(diào)用消息處理程序。

(6)使用BackgroundWorker組件

主要的事件及參數(shù):

1.DoWork—當(dāng)執(zhí)行BackgroundWorker.RunWorkerAsync方法時(shí)會(huì)觸發(fā)該事件,并且傳遞DoWorkEventArgs參數(shù);

2.ProgressChanged—操作處理中獲得的處理狀態(tài)變化,通過(guò)BackgroundWorker.ReportProgress方法觸發(fā)該事件,并且傳遞ProgressChangedEventArgs,其中包含了處理的百分比,這個(gè)參數(shù)在UI界面上設(shè)置progressbar控件。

3.RunWorkerCompleted—異步操作完成或中途終止會(huì)觸發(fā)該事件。如果需要提前終止執(zhí)行后臺(tái)操作,可以調(diào)用BackgroundWorker.CancelAsync方法。在處理DoWork事件的函數(shù)中檢測(cè)BackgroundWorker.CancellationPending屬性是否為true,如果是true,則表示用戶已經(jīng)取消了異步調(diào)用,同時(shí)將DoWorkEventArgs.Cancel屬性設(shè)為true(傳遞給處理DoWork事件的函數(shù)的第二個(gè)參數(shù)),這樣當(dāng)退出異步調(diào)用的時(shí)候,可以讓處理RunWorkerCompleted事件的函數(shù)知道是正常退出還是中途退出。

主要的方法:

1. BackgroundWorker.RunWorkerAsync—“起動(dòng)”異步調(diào)用的方法有兩次重載RunWorkerAsync(),RunWorkerAsync(object argument),第二個(gè)重載提供了一個(gè)參數(shù),可以供異步調(diào)用使用。(如果有多個(gè)參數(shù)要傳遞怎么辦,使用一個(gè)類來(lái)傳遞他們吧)。調(diào)用該方法后會(huì)觸發(fā)DoWork事件,并且為處理DoWork事件的函數(shù)傳遞DoWorkEventArg參數(shù),其中包含了RunWorkerAsync傳遞的參數(shù)。在相應(yīng)DoWork的處理函數(shù)中就可以做具體的復(fù)雜操作。

2. BackgroundWorker.ReportProgress—需要在一個(gè)冗長(zhǎng)的操作中向用戶不斷反饋進(jìn)度,這樣的話就可以調(diào)用的ReportProgress(int percent),在調(diào)用 ReportProgress 方法時(shí),觸發(fā)ProgressChanged事件。提供一個(gè)在 0 到 100 之間的整數(shù),它表示后臺(tái)活動(dòng)已完成的百分比。你也可以提供任何對(duì)象作為第二個(gè)參數(shù),允許你給事件處理程序傳遞狀態(tài)信息。作為傳遞到此過(guò)程的 ProgressChangedEventArgs 參數(shù)屬性,百分比和你自己的對(duì)象(如果提供的話)均要被傳遞到 ProgressChanged 事件處理程序。這些屬性被分別命名為 ProgressPercentage 和 UserState,并且你的事件處理程序可以以任何需要的方式使用它們。(注意:只有在BackgroundWorker.WorkerReportsProgress屬性被設(shè)置為true該方法才可用)。

3. BackgroundWorker.CancelAsync—但需要退出異步調(diào)用的時(shí)候,就調(diào)用的這個(gè)方法。但是樣還不夠,因?yàn)樗鼉H僅是將BackgroudWorker.CancellationPending屬性設(shè)置為true。你需要在具體的異步調(diào)用處理的時(shí)候,不斷檢查BackgroudWorker.CancellationPending是否為true,如果是真的話就退出。(注意:只有在BackgroundWorker.WorkerSupportsCancellation屬性被設(shè)置為true該方法才可用)。

復(fù)制代碼 代碼如下:

//按鈕事件

private void button1_Click(object sender, EventArgs e)

{

    this.backgroundWorker1.RunWorkerAsync();//處理事務(wù)

}

//處理事務(wù)事件

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

    //初始化進(jìn)度條

    this.progressBar1.Maximum = 100;

    this.progressBar1.Minimum = 0;

    //模擬事物處理

    for (int i = 0; i < 100; i++)

    {

        Thread.Sleep(10);

        //局部操作完成事件觸發(fā)

        this.backgroundWorker1.ReportProgress(i, null);

    }

}

//局部操作完成時(shí)執(zhí)行的方法

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

{

       this.progressBar1.Value = e.ProgressPercentage;//設(shè)置進(jìn)度條值

}

//事物處理完成時(shí)觸發(fā)

private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)

{

    MessageBox.Show(null, "工作線程完成!", "提示");

}


4.線程池

(1)線程池的作用

許多時(shí)候,我們需要用多線程,但是又不希望線程的數(shù)量過(guò)多,這就是線程池的作用,.Net為我們提供了現(xiàn)成的線程池ThreadPool。

(2)線程池的使用

復(fù)制代碼 代碼如下:

    class Program

    {

        //線程方法

        public static void ThreadProc(object i)

        {

            Console.WriteLine(i.ToString());

            Thread.Sleep(1000);

        }

        public static void Main()

        {

            ThreadPool.SetMaxThreads(3, 3);//設(shè)置線程池

            for (int i = 0; i < 10; i++)

            {

                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), "線程" + i);

            }

            Console.WriteLine("運(yùn)行結(jié)束");

            Console.Read();

        }

}


每一個(gè)進(jìn)程都有一個(gè)線程池,線程池的默認(rèn)大小是25,我們可以通過(guò)SetMaxThreads方法來(lái)設(shè)置其最大值。

注意:因?yàn)閃aitCallback委托的原型是void WaitCallback(object state),那沒(méi)有辦法,我們只能將多個(gè)參數(shù)封裝到一個(gè)Object中。

5.線程同步

(1)代碼塊同步(Monitor與lock )

1、說(shuō)明:

使用Monitor類的使用與lock關(guān)鍵字的使用在實(shí)現(xiàn)原理上相同。

Monitor.Enter 方法:在指定對(duì)象上獲取排他鎖。

Monitor.TryEnter 方法:試圖獲取指定對(duì)象的排他鎖。

Monitor.Exit 方法:釋放指定對(duì)象上的排他鎖。

Monitor.Wait 方法:釋放對(duì)象上的鎖并阻塞當(dāng)前線程,直到它重新獲取該鎖。

Monitor.Pulse 方法:通知等待隊(duì)列中的線程鎖定對(duì)象狀態(tài)的更改。

Monitor.PulseAll 方法:通知所有的等待線程對(duì)象狀態(tài)的更改。

2、示例

復(fù)制代碼 代碼如下:

    class Program

    {

        private int Count = 0;

        //線程執(zhí)行方法

        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是一個(gè)抽象類,下面是從它繼承來(lái)的幾個(gè)類:

Mutex:一個(gè)同步基元,也可用于進(jìn)程間同步。

AutoResetEvent:通知正在等待的線程已發(fā)生事件。無(wú)法繼承此類。

ManualResetEvent:通知一個(gè)或多個(gè)正在等待的線程已發(fā)生事件。無(wú)法繼承此類。

WaitHandle的幾個(gè)方法:

WaitAll:等待指定數(shù)組中的所有元素收到信號(hào)。

WaitAny:等待指定數(shù)組中的任一元素收到信號(hào)。

WaitOne:當(dāng)在派生類中重寫時(shí),阻塞當(dāng)前線程,直到當(dāng)前的WaitHandle收到信號(hào)。

(3)使用Mutex

1、使用Mutex控制線程同步

復(fù)制代碼 代碼如下:

    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("主線程執(zhí)行完畢");

            Console.ReadLine();

        }

        //線程執(zhí)行方法

        private static void Method(Object o)

        {

            mutex.WaitOne();//等待信號(hào)

            for (int i = 0; i < 3; i++)

            {

                Thread.Sleep(500);

                Console.WriteLine(o.ToString() + "循環(huán)" + i);

            }

            mutex.ReleaseMutex();//釋放信號(hào)

        }

    }


注意:對(duì)于WaitAll、WaitAny、WaitOne方法的使用請(qǐng)參考AutoResetEvent與ManualResetEvent。

2、使用Mutex控制進(jìn)程間的同步

復(fù)制代碼 代碼如下:

    class Program

    {

        static void Main(string[] args)

        {

            bool flag = false;

            Mutex mutex = new Mutex(true, "Test", out flag);

            //第一個(gè)參數(shù):true--給調(diào)用線程賦予互斥體的初始所屬權(quán)

            //第一個(gè)參數(shù):互斥體的名稱

            //第三個(gè)參數(shù):返回值,如果調(diào)用線程已被授予互斥體的初始所屬權(quán),則返回true

            if (flag)

            {

                Console.Write("進(jìn)程運(yùn)行...");

            }

            else

            {

                Console.Write("這個(gè)進(jìn)程正在運(yùn)行!");

                Thread.Sleep(5000);//線程掛起5秒鐘

                Environment.Exit(1);//退出程序

            }

            Console.ReadLine();

        }

    }


運(yùn)行以上代碼生成的應(yīng)用程序第一個(gè)實(shí)例,會(huì)得到結(jié)果:進(jìn)程運(yùn)行...

保持第一個(gè)運(yùn)行狀態(tài),運(yùn)行第二個(gè)實(shí)例,得到結(jié)果:這個(gè)進(jìn)程正在運(yùn)行!

注意:以上代碼中創(chuàng)建了一個(gè)mutex,從其參數(shù)的解釋中得知,第一個(gè)調(diào)用線程將得到互斥體的初始所屬權(quán),如果不釋放的話,其他的線程得不到互斥體所有權(quán)

(4)使用AutoResetEvent

1、使用WaitAll靜態(tài)方法

理解AutoResetEvent.WaitAll(Waits)靜態(tài)方法:WaitAll靜態(tài)方法就是阻塞當(dāng)前線程,直到Waits數(shù)組里的所有元素都調(diào)用Set()方法發(fā)送信號(hào),再繼續(xù)執(zhí)行當(dāng)前線程。

復(fù)制代碼 代碼如下:

    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()

                {

                    //線程執(zhí)行方法

                    Console.WriteLine("線程:" + temp);

                    Thread.Sleep(1000);

                    Waits[temp].Set();//發(fā)送線程執(zhí)行完畢信號(hào)

                };

                ThreadStart ts = new ThreadStart(thread);

                Thread t = new Thread(ts);

                t.Start();

            }

            AutoResetEvent.WaitAll(Waits);//等待Waits中的所有對(duì)象發(fā)出信號(hào)

            Console.WriteLine("線程全部執(zhí)行完畢!");

            Console.Read();

        }

    }


2、使用WaitAny靜態(tài)方法

理解AutoResetEvent.WaitAny(Waits)靜態(tài)方法:WaitAny靜態(tài)方法就是阻塞當(dāng)前線程,只要Waits數(shù)組有一個(gè)元素調(diào)用Set()方法發(fā)送信號(hào),就繼續(xù)執(zhí)行當(dāng)前線程。

復(fù)制代碼 代碼如下:

    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);//等待上一個(gè)線程執(zhí)行完畢

                    }

                    //線程執(zhí)行方法

                    Thread.Sleep(1000);

                    Waits[temp].Set();//發(fā)送線程執(zhí)行完畢信號(hào)

                    Console.WriteLine("線程:" + temp+"執(zhí)行完畢");

                };

                ThreadStart ts = new ThreadStart(thread);

                Thread t = new Thread(ts);

                t.Start();

            }

            Console.Read();

        }

    }


3、使用WaitOne成員方法

理解Wait.WaitOne()成員方法:WaitOne方法就是阻塞當(dāng)前線程,只要Wait對(duì)象調(diào)用了Set()方法發(fā)送信號(hào),就繼續(xù)執(zhí)行當(dāng)前線程。

復(fù)制代碼 代碼如下:

    class Program

    {

        public static void Main()

        {

            AutoResetEvent Wait = new AutoResetEvent(false);

            for (int i = 0; i < 10; i++)

            {

                Action thread = delegate()

                {

                    //線程執(zhí)行方法

                    Thread.Sleep(1000);

                    Wait.Set();//發(fā)送線程執(zhí)行完畢信號(hào)

                    Console.WriteLine("線程:" + i+"執(zhí)行完畢");

                };

                ThreadStart ts = new ThreadStart(thread);

                Thread t = new Thread(ts);

                t.Start();

                Wait.WaitOne();//等待調(diào)用 Waits.Set()

            }

            Console.Read();

        }

    }


(5)使用ManualResetEvent與AutoResetEvent的區(qū)別

1、ManualResetEvent與AutoResetEvent的相同點(diǎn)

對(duì)于WaitAll、WaitAny、WaitOne方法的使用ManualResetEvent與AutoResetEvent對(duì)象是沒(méi)有區(qū)別的。

2、ManualResetEvent與AutoResetEvent的區(qū)別

但是對(duì)于Set()方法AutoResetEvent只會(huì)給一個(gè)線程發(fā)送信號(hào),而ManualResetEvent會(huì)給多個(gè)線程發(fā)送信號(hào)。在我們需要同步多個(gè)線程的時(shí)候,就只能采用ManualResetEvent了。至于深層次的原因是,AutoResetEvent在Set()之后,會(huì)將線程狀態(tài)自動(dòng)置為false,而ManualResetEvent在Set()后,線程的狀態(tài)就變?yōu)閠rue了,必須手動(dòng)ReSet()之后,才會(huì)重新將線程置為false。這也就是為什么他們的名字一個(gè)為Auto(自動(dòng)),一個(gè)為Manual(手動(dòng))的原因。

復(fù)制代碼 代碼如下:

    class Program

    {

        private static ManualResetEvent Wait = new ManualResetEvent(false);

        public static void Main()

        {

            Wait.Set();//設(shè)置線程狀態(tài)為允許執(zhí)行

            Thread thread1 = new Thread(Method);

            thread1.Start("線程1");

            Thread.Sleep(1000);//等待線程1執(zhí)行

            Wait.Reset();//必須手動(dòng)復(fù)位線程狀態(tài),使?fàn)顟B(tài)為不允許執(zhí)行

            Thread thread2 = new Thread(Method);

            thread2.Start("線程2");//線程2將會(huì)一直等待信號(hào)

            Console.WriteLine("主線程結(jié)束");

            Console.Read();

        }

        //線程執(zhí)行方法

        private static void Method(Object o)

        {

            Wait.WaitOne();//等待信號(hào)

            Console.WriteLine(o.ToString());

        }

    }


(6)使用Interlocked進(jìn)行原子操作

Interlocked類為多個(gè)線程共享的變量提供原子操作。

原子操作:Interlocked.Increment()操作是一個(gè)原子操作,作用是:Count++ 。原子操作,就是不能被更高等級(jí)中斷搶奪優(yōu)先的操作。由于操作系統(tǒng)大部分時(shí)間處于開中斷狀態(tài),所以,一個(gè)程序在執(zhí)行的時(shí)候可能被優(yōu)先級(jí)更高的線程中斷。而有些操作是不能被中斷的,不然會(huì)出現(xiàn)無(wú)法還原的后果,這時(shí)候,這些操作就需要原子操作。就是不能被中斷的操作。

復(fù)制代碼 代碼如下:

    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);//休眠足夠的時(shí)間等待所有線程執(zhí)行完畢

            Console.WriteLine("操作后的結(jié)果:" + Program.Count);

            Console.ReadLine();

        }

        //線程執(zhí)行方法

        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進(jìn)行同步控制的問(wèn)題:由于獨(dú)占訪問(wèn)模型不允許任何形式的并發(fā)訪問(wèn),這樣的效率總是不太高。許多時(shí)候,應(yīng)用程序在訪問(wèn)資源時(shí)是進(jìn)行讀操作,寫操作相對(duì)較少。為解決這一問(wèn)題,C#提供了System.Threading.ReaderWriterLock類以適應(yīng)多用戶讀/單用戶寫的場(chǎng)景。該類可實(shí)現(xiàn)以下功能:如果資源未被寫操作鎖定,那么任何線程都可對(duì)該資源進(jìn)行讀操作鎖定,并且對(duì)讀操作鎖數(shù)量沒(méi)有限制,即多個(gè)線程可同時(shí)對(duì)該資源進(jìn)行讀操作鎖定,以讀取數(shù)據(jù)。如果資源未被添加任何讀或?qū)懖僮麈i,那么一個(gè)且僅有一個(gè)線程可對(duì)該資源添加寫操作鎖定,以寫入數(shù)據(jù)。簡(jiǎn)單的講就是:讀操作鎖是共享鎖,允許多個(gè)線程同時(shí)讀取數(shù)據(jù);寫操作鎖是獨(dú)占鎖,同一時(shí)刻,僅允許一個(gè)線程進(jìn)行寫操作。

ReaderWriterLock類:定義支持單個(gè)寫線程和多個(gè)讀線程的鎖。

ReaderWriterLockSlim類:表示用于管理資源訪問(wèn)的鎖定狀態(tài),可實(shí)現(xiàn)多線程讀取或進(jìn)行獨(dú)占式寫入訪問(wèn)。

復(fù)制代碼 代碼如下:

    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)//讀數(shù)據(jù)

        {

            rwl.AcquireReaderLock(1000 * 20); //申請(qǐng)讀操作鎖,在20s內(nèi)未獲取讀操作鎖,則放棄

            Console.WriteLine(o.ToString() + "讀取數(shù)據(jù):" + Program.Count);

            Thread.Sleep(500);

            rwl.ReleaseReaderLock();//釋放讀操作鎖

        }

        private static void Write(Object o)//寫數(shù)據(jù)

        {

            rwl.AcquireWriterLock(1000 * 20);//申請(qǐng)寫操作鎖,在20s內(nèi)未獲取寫操作鎖,則放棄

            Thread.Sleep(500);

            Console.WriteLine(o.ToString() + "寫數(shù)據(jù):" + (++Program.Count));

            rwl.ReleaseWriterLock();//釋放寫操作鎖

        }

    }


(8)使用Semaphore

Semaphore類:限制可同時(shí)訪問(wèn)某一資源或資源池的線程數(shù)。

復(fù)制代碼 代碼如下:

    class Program

    {

        private static Semaphore semaphore = new Semaphore(0, 5);//初始化信號(hào)量

        static void Main(string[] args)

        {

            for (int i = 0; i < 10; i++)

            {

                Thread thread = new Thread(Method);

                thread.Start("線程" + i);

            }

            semaphore.Release(2);//釋放信號(hào)量2個(gè)

            Console.WriteLine("主線程運(yùn)行完畢!");

            Console.Read();

        }

        //線程執(zhí)行方法

        private static void Method(object o)

        {

            semaphore.WaitOne();//等待信號(hào)量

            Thread.Sleep(1000);

            Console.WriteLine(o.ToString());

            semaphore.Release();//釋放信號(hào)量

        }

}


其它的線程只有等到主線程釋放才會(huì)執(zhí)行,因?yàn)槲医o信號(hào)量計(jì)數(shù)器的初始值是0,所以其它線程在主線程釋放前都會(huì)被阻塞。而后,我在主線程直接用Release(2)函數(shù)將計(jì)數(shù)器置為2,所以2個(gè)線程可以同時(shí)得到執(zhí)行。

注意:可以給信號(hào)量設(shè)置一個(gè)名稱,這個(gè)名稱是操作系統(tǒng)可見(jiàn)的,因此,可以使用這些信號(hào)量來(lái)協(xié)調(diào)跨進(jìn)程邊界的資源使用。

復(fù)制代碼 代碼如下:

    class Program

    {

        static void Main(string[] args)

        {

            //初始信號(hào)量5個(gè),最多信號(hào)量10個(gè)

            Semaphore seamphore = new Semaphore(5, 10, "Test");

            seamphore.WaitOne();//等待信號(hào)

            Console.WriteLine("獲取信號(hào)量 1");

            seamphore.WaitOne();//等待信號(hào)

            Console.WriteLine("獲取信號(hào)量 2");

            seamphore.WaitOne();//等待信號(hào)

            Console.WriteLine("獲取信號(hào)量 3");

            Console.WriteLine("主線程運(yùn)行完畢!");

            Console.Read();

        }

}


運(yùn)行兩個(gè)這樣的程序,結(jié)果如下,在第二個(gè)運(yùn)行的示例中,會(huì)將阻塞在第三個(gè)信號(hào)量上:



6.定時(shí)器Timer

(1)常用的3個(gè)Timer類

System.Threading.Timer 提供以指定的時(shí)間間隔執(zhí)行方法的機(jī)制。無(wú)法繼承此類。

System.Timers.Timer 在應(yīng)用程序中生成定期事件。

System.Windows.Forms.Timer 實(shí)現(xiàn)按用戶定義的時(shí)間間隔引發(fā)事件的計(jì)時(shí)器。此計(jì)時(shí)器最宜用于Windows窗體應(yīng)用程序中,并且必須在窗口中使用。

(2)System.Timers.Timer的使用示例

復(fù)制代碼 代碼如下:

    class Program

    {

        static void Main(string[] args)

        {

            System.Timers.Timer t = new System.Timers.Timer(1000);//產(chǎn)生事件的時(shí)間間隔1s

            t.Elapsed += new ElapsedEventHandler(Method); //到達(dá)時(shí)間的時(shí)候執(zhí)行事件

            t.AutoReset = true;//設(shè)置是執(zhí)行一次(false)還是一直執(zhí)行(true)

            t.Enabled = true;//是否執(zhí)行System.Timers.Timer.Elapsed事件

            Console.WriteLine("完成!");

            Console.Read();

        }

        private static void Method(object source,ElapsedEventArgs e)

        {

            Console.WriteLine("時(shí)間:"+e.SignalTime);

        }

    }

相關(guān)文章

最新評(píng)論