程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 線程系列04,傳遞數據給線程,線程命名,線程異常處理,線程池,04異常處理

線程系列04,傳遞數據給線程,線程命名,線程異常處理,線程池,04異常處理

編輯:C#入門知識

線程系列04,傳遞數據給線程,線程命名,線程異常處理,線程池,04異常處理


本篇體驗:如何傳遞數據給線程,如何給線程命名,線程的異常處理,線程池。實在是太基礎的部分。

 

□ 傳遞數據給線程

※ 使用Lambda表達式

    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(() => Say("hello", "world"));
            t.Start();
        }
        static void Say(string msg, string msg1)
        {
            Console.WriteLine("第一個參數值是:"+msg);
            Console.WriteLine("第二個參數值是:" + msg1);
        }
    }

 

使用Lambda表達式需要注意的一個問題。

    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                new Thread(() => Console.WriteLine("當前線程#" + Thread.CurrentThread.ManagedThreadId + "輸出的值是:" + i)).Start();
            }
        }
    }

說明,有些線程共享了局部變量i。如何解決線程共享棧變量的問題呢?只需要在每次遍歷循環的時候,把i賦值給一個臨時變量,再打印臨時變量,不直接打印棧變量i。

    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                int temp = i;
                new Thread(() => Console.WriteLine("當前線程#" + Thread.CurrentThread.ManagedThreadId + "輸出的值是:" + temp)).Start();
            }
        }
    }


打印的值沒有重復的。


※ 使用線程的實例方法Start

    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(Say);
            t.Start("hello");
        }
        static void Say(object msg)
        {
            string str = (string) msg;
            Console.WriteLine(str);
        }
    }

 

之所以可以這樣做,是因為Thread的構造函數可以接收2種類型的形參。

public delegate void ThreadStart();
public delegate void ParameterizedThreadStart (object obj);

所有符合ParameterizedThreadStart這個委托定義的方法,必須有一個object類型的參數,然後在實例方法Start中傳遞參數。使用這種方式傳遞參數的弊端是:

1、只能傳遞一個參數
2、在方法體中,每次都要對object類型進行轉換,拆箱

 

□ 給線程命名

    class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "主線程";
            Thread t = new Thread(Say);
            t.Name = "其它工作線程";
            t.Start();
            Say();
        }
        static void Say()
        {
            Console.WriteLine("我的名字是:" + Thread.CurrentThread.Name);
        }
    }

通過Name屬性可以設置或獲取當前線程和實例線程的名稱。

 

□ 線程的異常處理

一個線程無法捕獲另外線程的異常。

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                new Thread(Say).Start();
            }
            catch (Exception)
            {
                Console.WriteLine("有異常");
            }
        }
        static void Say()
        {
            throw null;
        }
    }


以上,在主線程的catch語句塊中,無法捕獲另外一個線程的異常。

 

實際上,一個線程的異常必須在該線程方法內捕獲。

    class Program
    {
        static void Main(string[] args)
        {
            new Thread(Say).Start();
        }
        static void Say()
        {
            try
            {
                throw null;
            }
            catch (Exception)
            {
                Console.WriteLine("捕獲到異常了");
            }
        }
    }

 

□ 線程池

當創建一個線程,會用幾十到幾百毫秒的時間創建該線程的棧,而且,在默認情況下,每個線程需要1M的棧空間。線程池做到了共享和重復使用線程,性能得以提高。線程池還可以設置允許的最多線程數,一旦達到這個極限值,多余的線程需要排隊,等待線程池中的線程執行結束再進入。

 

線程池的默認的最大線程數因計算機不同而異。在.NET 4.0及其以後的版本中,在2位計算機最大線程數為1023個,64位為32768;在.NET3.5中,線程池最大線程數是250個;在.NET2.0中,線程池最大線程數為25個。當然,可以通過TreadPool.SetMaxThreads來設置線程池的最大線程數,也可以通過ThreadPool.SetMinThreads來設置最小線程數。

 

線程池中線程的IsBackground屬性默認為true。這意味著:主線程結束,應用程序結束,所有線程池中的後台線程結束。

 

※ 通過TPL進入線程池

TPL,Task Parallel Library,是一個處理並行任務的一個類庫。

    class Program
    {
        static void Main(string[] args)
        {
            Task tast = Task.Factory.StartNew(Say);
            tast.Wait();
        }
        static void Say()
        {
            Console.WriteLine("hello,我已經是線程池中的線程了");
        }
    }

 

使用System.Net.WebClient的實例方法DownloadString方法下載某個網頁,使用泛型Task<T>來處理。

    class Program
    {
        static void Main(string[] args)
        {
            Task<string> task = Task.Factory.StartNew<string>(() => DownloadString("http://www.baidu.com"));
            Console.WriteLine("Task在工作呢,我玩會啊~~");
            Console.WriteLine("Task在工作呢,我玩會啊~~");
            Console.WriteLine("Task在工作呢,我玩會啊~~");
            string result = task.Result;
            Console.WriteLine(result);
        }
        static string DownloadString(string uri)
        {
            using (var wc = new System.Net.WebClient())
            {
                return wc.DownloadString(uri);
            }
        }
    }

 

※ 不通過TPL進入線程池

    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(Say);
            ThreadPool.QueueUserWorkItem(Say, 123);
            Console.ReadLine();
        }
        static void Say(object data)
        {
            Console.WriteLine("我來自線程池,我獲取到的數據是:" + data);
        }
    }

  

 

※ 使用委托進入線程池

    class Program
    {
        static void Main(string[] args)
        {
            Func<string, int> method = GetLength;
            IAsyncResult ia = method.BeginInvoke("test", null, null);
            Console.WriteLine("線程池的線程在工作者呢,我先玩會~");
            int result = method.EndInvoke(ia);
            Console.WriteLine("test的長度為:" + result);
        }
        static int GetLength(string s)
        {
            return s.Length;
        }
    }

把方法賦值給委托變量,CLR會創建一個線程池中的線程。

 

線程系列包括:

線程系列01,前台線程,後台線程,線程同步

線程系列02,多個線程同時處理一個耗時較長的任務以節省時間

線程系列03,多線程共享數據,多線程不共享數據

線程系列04,傳遞數據給線程,線程命名,線程異常處理,線程池

線程系列05,手動結束線程

 


參考資料:http://www.albahari.com/threading


怎給線程池裡的線程命名

LZ是自己繼承並實現execute方法嗎?還是使用Java提供的這個抽象類的一個實現類ThreadPoolExecutor?如果是用ThreadPoolExecutor,可以去看下ThreadFactory這接口
 

C#管理大量耗時的線程,內存占用嚴重

在你的表述中,“線程池不適合用來做耗時的任務”是最大誤區
1)你一定看到過System.Net.Socket類中有很多BeginXXX / EndXXX的方法,例如Socket.BeginReceiveFrom和Socket.EndReceiveFrom,這些函數統稱為異步函數。而異步函數操作的基礎恰恰就是線程池。對Socket通信而言,微軟提供的異步操作正是利用線程池中I/O線程,目的就是為了提高Socket I/O性能並簡化內存管理的!
2)如果對異步操作感到頭暈,在處理Socket操作時可以使用“顯式線程”方法,即按以下方法啟動處理線程:
System.Thread t = new System.Thread(你的處理函數);
t.IsBackground = true; //這個設置尤其重要!!!!!
t.Start();
一定要注意將線程設置為後台線程!
3)你一定知道系統每次啟動線程和銷毀線程時都會導致很大的開銷。當你的程序頻繁的啟動、銷毀線程,必然會導致程序很“卡”;正是由於這個緣故,微軟才搞了一個“線程池”。因為線程中的線程都是“啟動完畢的”(這樣表述雖不確切,但沒有錯),一旦你將異步處理函數“掛接”的線程池中的空閒線程上即可以執行你要的操作。而且,額外的好處是你根本不用去管理線程池中的線程(真正的“零”管理)
4)處理“耗時的操作”特別是涉及諸如Socket I/O 耗時操作,最佳的處理方法是利用後台線程(如果需要,同時配合以自定義事件event),這是增加用戶體驗不二法門哦~~
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved