C#多線程編程


一、使用線程的理由
1、可以使用線程將代碼同其他代碼隔離,提高應用程序的可靠性。
2、可以使用線程來簡化編碼。
3、可以使用線程來實現並發執行。
二、基本知識
1、進程與線程:進程作為操作系統執行程序的基本單位,擁有應用程序的資源,進程包含線程,進程的資源被線程共享,線程不擁有資源。
2、前台線程和后台線程:通過Thread類新建線程默認為前台線程。當所有前台線程關閉時,所有的后台線程也會被直接終止,不會拋出異常。
3、掛起(Suspend)和喚醒(Resume):由於線程的執行順序和程序的執行情況不可預知,所以使用掛起和喚醒容易發生死鎖的情況,在實際應用中應該盡量少用。
4、阻塞線程:Join,阻塞調用線程,直到該線程終止。
5、終止線程:Abort:拋出 ThreadAbortException 異常讓線程終止,終止后的線程不可喚醒。Interrupt:拋出 ThreadInterruptException 異常讓線程終止,通過捕獲異常可以繼續執行。
6、線程優先級:AboveNormal BelowNormal Highest Lowest Normal,默認為Normal。
三、線程的使用
線程函數通過委托傳遞,可以不帶參數,也可以帶參數(只能有一個參數),可以用一個類或結構體封裝參數。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(new ThreadStart(TestMethod));
            Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
            t1.IsBackground = true;
            t2.IsBackground = true;
            t1.Start();
            t2.Start("hello");
            Console.ReadKey();
        }

        public static void TestMethod()
        {
            Console.WriteLine("不帶參數的線程函數");
        }

        public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine("帶參數的線程函數,參數為:{0}", datastr);
        }
    } 
}

四、線程池
由於線程的創建和銷毀需要耗費一定的開銷,過多的使用線程會造成內存資源的浪費,出於對性能的考慮,於是引入了線程池的概念。線程池維護一個請求隊列,線程池的代碼從隊列提取任務,然后委派給線程池的一個線程執行,線程執行完不會被立即銷毀,這樣既可以在后台執行任務,又可以減少線程創建和銷毀所帶來的開銷。
線程池線程默認為后台線程(IsBackground)。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            //將工作項加入到線程池隊列中,這里可以傳遞一個線程參數
            ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
            Console.ReadKey();
        }

        public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine(datastr);
        }
    }
}

五、Task類
使用ThreadPool的QueueUserWorkItem()方法發起一次異步的線程執行很簡單,但是該方法最大的問題是沒有一個內建的機制讓你知道操作什么時候完成,有沒有一個內建的機制在操作完成后獲得一個返回值。為此,可以使用System.Threading.Tasks中的Task類。
構造一個Task<TResult>對象,並為泛型TResult參數傳遞一個操作的返回類型。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
            t.Start();
            t.Wait();
            Console.WriteLine(t.Result);
            Console.ReadKey();
        }

        private static Int32 Sum(Int32 n)
        {
            Int32 sum = 0;
            for (; n > 0; --n)
                checked{ sum += n;} //結果太大,拋出異常
            return sum;
        }
    }
}

一個任務完成時,自動啟動一個新任務。
一個任務完成后,它可以啟動另一個任務,下面重寫了前面的代碼,不阻塞任何線程。

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
            t.Start();
            //t.Wait();
            Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}",t.Result));
            Console.ReadKey();
        }

        private static Int32 Sum(Int32 n)
        {
            Int32 sum = 0;
            for (; n > 0; --n)
                checked{ sum += n;} //結果溢出,拋出異常
            return sum;
        }
    }
}

六、委托異步執行
委托的異步調用:BeginInvoke() 和 EndInvoke()

namespace Test
{
    public delegate string MyDelegate(object data);
    class Program
    {
        static void Main(string[] args)
        {
            MyDelegate mydelegate = new MyDelegate(TestMethod);
            IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param");

            //異步執行完成
            string resultstr = mydelegate.EndInvoke(result);
        }

        //線程函數
        public static string TestMethod(object data)
        {
            string datastr = data as string;
            return datastr;
        }

        //異步回調函數
        public static void TestCallback(IAsyncResult data)
        {
            Console.WriteLine(data.AsyncState);
        }
    }
}

七、線程同步
  1)原子操作(Interlocked):所有方法都是執行一次原子讀取或一次寫入操作。
  2lock()語句:避免鎖定public類型,否則實例將超出代碼控制的范圍,定義private對象來鎖定。
  3)Monitor實現線程同步
    通過Monitor.Enter() 和 Monitor.Exit()實現排它鎖的獲取和釋放,獲取之后獨占資源,不允許其他線程訪問。
    還有一個TryEnter方法,請求不到資源時不會阻塞等待,可以設置超時時間,獲取不到直接返回false。
  4)ReaderWriterLock
    當對資源操作讀多寫少的時候,為了提高資源的利用率,讓讀操作鎖為共享鎖,多個線程可以並發讀取資源,而寫操作為獨占鎖,只允許一個線程操作。
  5)事件(Event)類實現同步
    事件類有兩種狀態,終止狀態和非終止狀態,終止狀態時調用WaitOne可以請求成功,通過Set將時間狀態設置為終止狀態。
    1)AutoResetEvent(自動重置事件)
    2)ManualResetEvent(手動重置事件)
  6)信號量(Semaphore)
      信號量是由內核對象維護的int變量,為0時,線程阻塞,大於0時解除阻塞,當一個信號量上的等待線程解除阻塞后,信號量計數+1。
      線程通過WaitOne將信號量減1,通過Release將信號量加1,使用很簡單。
  7)互斥體(Mutex)
      獨占資源,用法與Semaphore相似。
   8)跨進程間的同步
      通過設置同步對象的名稱就可以實現系統級的同步,不同應用程序通過同步對象的名稱識別不同同步對象。
View Code

帶返回值的委托

        delegate bool GCaller(string uid, string Tuid, string LY, CookieContainer Cookiecc);//cookie代理 
        public bool GetName(string uid,string Tuid, string LY, CookieContainer Cookiecc)    // 函數
        {
            
        }

使用方法
            GCaller Snd = new GCaller(GetName);
            IAsyncResult Sndresult = Snd.BeginInvoke(uid, Tuid, LY,Cookiecc, null, null);
            bool rsw = Snd.EndInvoke(Sndresult);//用於接收返回值 
View Code

控件顯示值設置

#region 委托方式線程更改進程條值

        public delegate void SetValue(ProgressBar C, int Value);

        public void DoSetValue(ProgressBar C, int Value)
        {
            C.Value =  Value;
        }
        public void SetValueWithDelegate(ProgressBar C, int Value)
        {
            try
            {
                SetValue PSetValue = new SetValue(DoSetValue);
                if (C.InvokeRequired)
                    C.Invoke(PSetValue, new Object[] { C, Value });
                else
                    C.Value = Value;
            }
            catch { }
        }
        #endregion
        #region 委托方式線程更改控件文本
        //  SetTextWithDelegate(需要操作的控件, "需要修改的文本");  
        public delegate void SetText(Control C, String Text);

        public void DoSetText(Control C, String Text)
        {
            C.Text = Text;
        }
      public   void SetTextWithDelegate(Control C, String Text)
        {
            try
            {
                SetText PSetText = new SetText(DoSetText);
                if (C.InvokeRequired)
                    C.Invoke(PSetText, new Object[] { C, Text });
                else
                    C.Text = Text;
            }
            catch { }
        }
        #endregion
View Code

C#2005 控件線程安全問題..委托

public Thread t1;
        delegate void DoThingCallBack();
        private void button1_Click(object sender, EventArgs e)
        {
              t1 = new Thread(new ThreadStart(caozuo1));
              t1.Start();
        }
        private void caozuo1()
        {
            if (webBrowser1.InvokeRequired)
            {
                DoThingCallBack cb = new DoThingCallBack(caozuo1);
                this.Invoke(cb);
            }
            else
            {
                HtmlElement guanli = webBrowser1.Document.All["Login"];
            }
        }

代碼有判斷webBrowser1.InvokeRequired,也就是當需要的時候才使用委托,而不是任何時候都使用。
View Code

C#發揮匿名委托的威力

 這幾天研究了一下Linq,C# 3.0中的“擴展方法”特性為IEnumerable<T>增加了諸如Where、Select等查詢方法,這使得“語言集成查詢”成為順其自然的事情。而C#3.0中Linq的實現也是建立在C#2.0的匿名委托的特性之上。
   今天,我嘗試在C#2.0中使用匿名委托模擬C#3.0中Where、Select等查詢方法的實現。我將所有的查詢方法作為靜態方法在GenericHepler靜態類中實現。
   之前,我們先定義泛型委托:
   public delegate TResult Func<T, TResult>(T source);
  這個委托在后面的實現中需要用到。

   作為基礎,首先,我們需要實現ForSpecification方法,該方法的含義是:對集合中滿足指定條件的元素執行指定方法調用。
 
         /// <summary>

        /// ForSpecification 對集合中滿足predicate條件的元素執行action。如果沒有條件,predicate傳入null。

        /// </summary>       

        public static void ForSpecification<TSource>(IEnumerable<TSource> collection, Action<TSource> action, Predicate<TSource> predicate)

        {

            if (predicate == null)

            {

                foreach (TSource obj in collection)

                {

                    action(obj);

                }


                return;

            }


            foreach (TSource obj in collection)

            {

                if (predicate(obj))

                {

                    action(obj);

                }

            }

        }
   有了ForSpecification的實現,我們就可以在其基礎上實現ForEach和ForFirstSpecification:
 
       #region ForEach

        /// <summary>

        /// ForEach  對集合中的每個元素執行action。

        /// </summary>        

        public static void ForEach<TSource>(IEnumerable<TSource> collection, Action<TSource> action)

        {

            GenericHepler.ForSpecification<TSource>(collection, action, null);

        }

        #endregion


        #region ForFirstSpecification

        /// <summary>

        /// ForSpecification 對集合中第一個滿足predicate條件的元素執行action。如果沒有條件,predicate傳入null。

        /// </summary>       

        public static void ForFirstSpecification<TSource>(IEnumerable<TSource> collection, Action<TSource> action, Predicate<TSource> predicate)

        {

            if (predicate == null)

            {

                foreach (TSource obj in collection)

                {

                    action(obj);

                    break;

                }

            }

            else

            {

                foreach (TSource obj in collection)

                {

                    if (predicate(obj))

                    {

                        action(obj);

                        break;

                    }

                }

            }

        }

        #endregion
   有了ForSpecification,我們就可以實現查詢方法Where:
       #region Where

        /// <summary>

        /// Where 從集合中選取符合條件的元素

        /// </summary>       

        public static IList<TSource> Where<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate)

        {

            IList<TSource> list = new List<TSource>();

            GenericHepler.ForSpecification(source, delegate(TSource ele) { list.Add(ele); } , predicate);

            return list;

        }

        #endregion
   對於C#3.0中的Select方法,其實現需要匿名類型的支持,而C#2.0中不支持匿名類型,所以,我用泛型來代替。我使用ConvertSpecification來模擬Select實現:
       #region ConvertSpecification

        /// <summary>

        /// ConvertSpecification 將source中的符合predicate條件元素轉換為TResult類型

        /// </summary>       

        public static IList<TResult> ConvertSpecification<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> converter, Predicate<TSource> predicate)

        {

            IList<TResult> list = new List<TResult>();

            GenericHepler.ForSpecification<TSource>(source, delegate(TSource ele) { list.Add(converter(ele)); } ,predicate);

            return list;

        }

        #endregion
   converter委托用於從TSource類型對象構造TResult類型的對象。
   有了ConvertSpecification實現,我們就可以在其上繼續實現ConvertAll和ConvertFirstSpecification:
       #region ConvertAll

        /// <summary>

        /// ConvertAll 將source中的每個元素轉換為TResult類型

        /// </summary>       

        public static IList<TResult> ConvertAll<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> converter)

        {

            return GenericHepler.ConvertSpecification<TSource, TResult>(source, converter, null);

        }

        #endregion


        #region ConvertFirstSpecification

        /// <summary>

        /// ConvertSpecification 將source中的符合predicate條件的第一個元素轉換為TResult類型

        /// </summary>       

        public static TResult ConvertFirstSpecification<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> converter, Predicate<TSource> predicate)

        {

            TSource target = GenericHepler.GetFirstSpecification<TSource>(source, predicate);


            if (target == null)

            {

                return default(TResult);

            }


            return converter(target);

        }

        #endregion       
   有了上面的基礎,我們還可以實現ContainsSpecification方法:
       #region ContainsSpecification

        /// <summary>

        /// ContainsSpecification 集合中是否包含滿足predicate條件的元素。

        /// </summary>       

        public static bool ContainsSpecification<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate, out TSource specification)

        {

            specification = default(TSource);

            foreach (TSource element in source)

            {

                if (predicate(element))

                {

                    specification = element;

                    return true;

                }

            }


            return false;

        }

        #endregion        


        #region ContainsSpecification

        /// <summary>

        /// ContainsSpecification 集合中是否包含滿足predicate條件的元素。

        /// </summary>       

        public static bool ContainsSpecification<TSource>(IEnumerable<TSource> source, Predicate<TSource> predicate)

        {

            TSource specification;

            return GenericHepler.ContainsSpecification<TSource>(source, predicate, out specification);

        }

        #endregion       
   代碼中的注釋已經將各個方法的用途說得非常清楚,下面我們舉兩個例子來看看如何使用它們以發揮它們的威力!
   例子一:比如,我們要從當前玩家(IPlayer)列表中找出所有年齡大於30歲的玩家的ID,通常這樣做:
        public IList<string> GetOldPlayer()

        {

            IList<string> results = new List<string>();

            foreach (IPlayer player in this.playerList)

            {

                if (player.Age > 30)

                {

                    results.Add(player.ID);

                }

            }


            return results;

        }
   如果使用上面我們封裝的API,則可以非常簡單地達到目的:
 public IList<string> GetOldPlayer()

 {

     return GenericHepler.ConvertSpecification<IPlayer, string>(this.playerList, delegate(IPlayer player) { return player.ID; } , delegate(IPlayer player) {return player.Age > 30 });            

 }
  一句搞定。
   
   例子二:我們要從當前的玩家字典(Dictionary)中取出所有ID不是指定集合中的ID的其它玩家列表。
   通常,我們可以這樣做:
        public IList<IPlayer> GetPartners(params string[] excludedUserIDs)

        {

            IList<IPlayer> partnersList = new List<IPlayer>();

            foreach (string userID in this.dicPlayers.Keys)

            {

                bool exclude = false;

                foreach (string excludedUser in excludedUserIDs)

                {

                    if (userID == excludedUser)

                    {

                        exclude = true;

                        break;

                    }

                }


                if (!exclude)

                {

                    partnersList.Add(this.dicPlayers[userID]);

                }

            }

            return partnersList;

        }
   使用上面我們封裝的API,則非常簡單:
 public IList<IPlayer> GetPartners(params string[] excludedUserIDs)

 {

     return GenericHepler.Where<IPlayer>(this.dicPlayers.Values, delegate(IPlayer player) { return !GenericHepler.ContainsSpecification<string>(excludedUserIDs, delegate(string id) { return id == player.UserID; }); });                            

 }
   靈活地使用這些API,我們可以非常簡潔地操作集合中的元素。
   最后給出GenericHepler類的源碼下載,其中還包含了幾個未介紹的實用的API。files.cnblogs.com/zhuweisky/GenericHepler.rar
 
View Code

C#線程類的定義實例解析

C#線程類的定義實例:

//C#線程類的定義實例  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading;  
namespace MyThread  
{  
   abstract class MyThread  
    {  
       Thread thread = null;  
 
       abstract public void run();  
 
        public void start()  
        {  
            if (thread == null)  
                thread = new Thread(run);  
            thread.Start();  
        }  
    }  
}  
    C#線程類的定義之使用MyThread類:

class NewThread : MyThread  
{  
      override public void run()  
      {  
      Console.WriteLine("使用MyThread建立並運行線程");  
      }  
  }  
 
  static void Main(string[] args)  
  {  
 
      NewThread nt = new NewThread();  
      nt.start();  
  }  
    C#線程類的定義實例基本的內容就向你介紹到這里,希望對你了解和學習C#線程類的定義有所幫助。
View Code

概述C#線程功能

C#線程功能

    線程是允許進行並行計算的一個抽象概念:在另一個線程完成計算任務的同時,一個線程可以對圖像進行更新,二個線程可以同時處理同一個進程發出的二個網絡請求。 

    從概念上講,線程提供了一種在一個軟件中並行執行代碼的方式━━每個線程都“同時”在一個共享的內存空間中執行指令,(當然是在一個處理器上,這是通過處於運行狀態的線程的交替執行完成的。),因此,每個線程都可以訪問一個程序內的數據結構。由於這種原因,多線程編程的難度就可想而知了,因為一個程序內有許多不同的線程需要安全地共享數據。   

線程的創建和運行   

    Java在java.lang.Thread和java.lang.Runnable類中提供了大部分的C#線程功能。創建一個線程非常簡單,就是擴展 Thread類,並調用start()。通過創建一個執行Runnable()的類,並將該類作為參數傳遞給Thread(),也可以定義一個線程。仔細地閱讀下面這個簡單的Java程序,其中有2個線程同時在從1數到5,並將結果打印出來。

public class ThreadingExample  
extends Object {  
public static void main( String args[] ) {  
Thread[] threads = new Thread[2];  
for( int count=1;count<=threads.length;count ) {  
threads[count] = new Thread( new Runnable() {  
public void run() {  
count();  
}  
} );  
threads[count].start();  
}  
}  
public static void count() {  
for( int count=1;count<=5;count )  
System.out.print( count " " );  
}  
} 
    我們可以使用System.Threading.Thread和System.Threading.ThreadStart二個類將上述的Java程序轉換為C#語言:

using System.Threading;  
public class ThreadingExample : Object {  
public static void Main() {  
Thread[] threads = new Thread[2];  
for( int count=1;count<=threads.Length;count ) {  
threads[count] = new Thread( new ThreadStart( Count ) );  
threads[count].Start();  
}  
}  
public static void Count() {  
for( int count=1;count<=5;count )  
Console.Write( count " " );  
}  
} 
    這個例子中有一些小技巧。Java允許擴展java.lang.Thread類和執行java.lang.Runnable接口,C#則沒有為我們提供這些便利。一個C#中的Thread對象是不可知的,必須通過ThreadStart進行創建,這意味着不能使用內部的類模式,而必須創建一個對象,而且必須傳遞給線程一個對象的方法供線程執行用。以上介紹C#線程功能。
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM