a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 編程 > C# > 正文

c#多線程的應用全面解析

2020-01-24 02:49:47
字體:
來源:轉載
供稿:網(wǎng)友

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

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

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

復制代碼 代碼如下:

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();//隨機數(shù)對象

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

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

        }

}


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

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

    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();//隨機數(shù)對象

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

            Console.WriteLine(arg);

        }

}


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

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

復制代碼 代碼如下:

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

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

        //構造函數(shù)

        public MyThread(int parame)

        {

            this.Parame = parame;

        }

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

        public void Calculate()

        {

            Random ra = new Random();//隨機數(shù)對象

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

            Console.WriteLine(this.Parame);

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

        }

}


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

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

復制代碼 代碼如下:

    class Program

    {

        static void Main(string[] args)

        {

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

            int Result = 0;//當做返回值

            //匿名方法

            ThreadStart threadStart = new ThreadStart(delegate()

            {

                Random ra = new Random();//隨機數(shù)對象

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

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

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

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

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

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

            while (!asyncResult.IsCompleted)

            {

                Console.Write("*");

                Thread.Sleep(100);

            }

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

            int result = task.EndInvoke(asyncResult);

            Console.WriteLine(result);

            Console.Read();

        }

    }


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

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

復制代碼 代碼如下:

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

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

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

            {

                Console.Write("*");

            }

            int result = task.EndInvoke(asyncResult);

            Console.WriteLine(result);

            Console.Read();

        }

    }


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

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

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

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

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

復制代碼 代碼如下:

    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("任務完成,結果:" + 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

    {

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

        }

        static void Main(string[] args)

        {

            HttpWebRequest request =

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

            //異步請求

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

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

            Console.Read();

        }

    }


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

(1)前臺線程與后臺線程

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

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

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

c.當初始化一個線程,把Thread.IsBackground=true的時候,指示該線程為后臺線程。后臺線程將會隨著主線程的退出而退出。

d.原理:只要所有前臺線程都終止后,CLR就會對每一個活在的后臺線程調(diào)用Abort()來徹底終止應用程序

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

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

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

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

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

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

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

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

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

3.多線程訪問GUI界面的處理

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

在GUI編程時,如果你從非創(chuàng)建這個控件的線程中訪問這個控件或者操作這個控件的話就會拋出這個異常。這是微軟為了保證線程安全以及提高代碼的效率所做的改進,但是也給大家?guī)砗芏嗖槐恪?/P>

(2)通過設置處理

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

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

復制代碼 代碼如下:

        //按鈕事件

        private void button1_Click(object sender, EventArgs e)

        {

            Thread thread = new Thread(Flush);

            thread.IsBackground = true;//設置成后臺線程

            thread.Start();

        }

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

        private void Flush()

        {

            //定義委托

            Action action = delegate()

            {

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

            };

            while (true)

            {

                //判斷能否到當前線程操作該組件

                if (this.textBox1.InvokeRequired)

                {

                    //不在當前線程上操作

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

                }

                else

                {

                    //在當前線程上操作

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

                }

                Thread.Sleep(1000);

            }


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

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

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

而所謂的“一面響應操作,一面添加節(jié)點”永遠只能是相對的,使UI線程的負擔不至于太大而以,因為界面的正確更新始終要通過UI線程去做,我們要做的事情是在工作線程中包攬大部分的運算,而將對純粹的界面更新放到UI線程中去做,這樣也就達到了減輕UI線程負擔的目的了。

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

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

(6)使用BackgroundWorker組件

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

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

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

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

主要的方法:

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

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

3. BackgroundWorker.CancelAsync―但需要退出異步調(diào)用的時候,就調(diào)用的這個方法。但是樣還不夠,因為它僅僅是將BackgroudWorker.CancellationPending屬性設置為true。你需要在具體的異步調(diào)用處理的時候,不斷檢查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);

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

        this.backgroundWorker1.ReportProgress(i, null);

    }

}

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

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

{

       this.progressBar1.Value = e.ProgressPercentage;//設置進度條值

}

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

private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)

{

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

}


4.線程池

(1)線程池的作用

許多時候,我們需要用多線程,但是又不希望線程的數(shù)量過多,這就是線程池的作用,.Net為我們提供了現(xiàn)成的線程池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),那沒有辦法,我們只能將多個參數(shù)封裝到一個Object中。

5.線程同步

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

1、說明:

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

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

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

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

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

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

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

2、示例

復制代碼 代碼如下:

    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是一個抽象類,下面是從它繼承來的幾個類:

Mutex:一個同步基元,也可用于進程間同步。

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

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

WaitHandle的幾個方法:

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

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

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

            Console.ReadLine();

        }

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

        private static void Method(Object o)

        {

            mutex.WaitOne();//等待信號

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

            {

                Thread.Sleep(500);

                Console.WriteLine(o.ToString() + "循環(huán)" + 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);

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

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

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

            if (flag)

            {

                Console.Write("進程運行...");

            }

            else

            {

                Console.Write("這個進程正在運行!");

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

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

            }

            Console.ReadLine();

        }

    }


運行以上代碼生成的應用程序第一個實例,會得到結果:進程運行...

保持第一個運行狀態(tài),運行第二個實例,得到結果:這個進程正在運行!

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

(4)使用AutoResetEvent

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

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

復制代碼 代碼如下:

    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í)行完畢信號

                };

                ThreadStart ts = new ThreadStart(thread);

                Thread t = new Thread(ts);

                t.Start();

            }

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

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

            Console.Read();

        }

    }


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

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

復制代碼 代碼如下:

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

                    }

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

                    Thread.Sleep(1000);

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

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

                };

                ThreadStart ts = new ThreadStart(thread);

                Thread t = new Thread(ts);

                t.Start();

            }

            Console.Read();

        }

    }


3、使用WaitOne成員方法

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

復制代碼 代碼如下:

    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í)行完畢信號

                    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的相同點

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

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

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

復制代碼 代碼如下:

    class Program

    {

        private static ManualResetEvent Wait = new ManualResetEvent(false);

        public static void Main()

        {

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

            Thread thread1 = new Thread(Method);

            thread1.Start("線程1");

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

            Wait.Reset();//必須手動復位線程狀態(tài),使狀態(tài)為不允許執(zhí)行

            Thread thread2 = new Thread(Method);

            thread2.Start("線程2");//線程2將會一直等待信號

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

            Console.Read();

        }

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

        private static void Method(Object o)

        {

            Wait.WaitOne();//等待信號

            Console.WriteLine(o.ToString());

        }

    }


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

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

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

復制代碼 代碼如下:

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

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

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

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

復制代碼 代碼如下:

    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); //申請讀操作鎖,在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);//申請寫操作鎖,在20s內(nèi)未獲取寫操作鎖,則放棄

            Thread.Sleep(500);

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

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

        }

    }


(8)使用Semaphore

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

復制代碼 代碼如下:

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

        }

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

        private static void Method(object o)

        {

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

            Thread.Sleep(1000);

            Console.WriteLine(o.ToString());

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

        }

}


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

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

復制代碼 代碼如下:

    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 提供以指定的時間間隔執(zhí)行方法的機制。無法繼承此類。

System.Timers.Timer 在應用程序中生成定期事件。

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

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

復制代碼 代碼如下:

    class Program

    {

        static void Main(string[] args)

        {

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

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

            t.AutoReset = true;//設置是執(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("時間:"+e.SignalTime);

        }

    }

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 国产日韩欧美激情 | 久久男女视频 | 久久九九99 | 欧美a√ | 国产精品视频在线观看 | 国产一级淫免费播放m | 国产成人在线免费观看视频 | 日韩精品视频在线播放 | 欧美精品一区二区三区一线天视频 | 日韩欧美视频在线 | 亚洲三级网站 | 国产91亚洲精品 | 亚洲精品久久久久久下一站 | 久久精品久久精品国产大片 | 娇小12—13毛片| 日韩在线成人 | 欧美色图网站 | 亚洲大胆人体视频 | 91观看| 凹凸日日摸日日碰夜夜爽孕妇 | 欧美激情自拍偷拍 | 国产精品三级在线 | 黄色片在线免费观看 | 久久美女 | 精品国产乱码久久久久久丨区2区 | 亚洲国产一区二区三区 | 国产伦精品一区二区三区四区视频 | www精品美女久久久tv | 黄色网址免费在线 | 国产.com | 视频在线一区 | 999视频在线免费观看 | 久久精品一级 | 久久精品国产亚 | 国产成人精品网 | 婷婷免费在线观看 | 欧美日韩精品一区二区三区在线观看 | 成人精品视频99在线观看免费 | 日韩激情在线观看 | 999国产| 久久99精品久久久久久秒播放器 |