《CLR via C#》讀書筆記-.NET多線程(六)

parallel處理
當存在以下情況:
1、需處理多個獨立方法
2、各方法之間不存在共享資源的情況
3、各方法可以使用相同的委託
就可以使用Parallel類的相關方法進行處理
以下是官網上的一個例子,

using System.Threading.Tasks;   
class Test
{
    static int N = 1000;

    static void TestMethod()
    {
        // Using a named method.
        Parallel.For(0, N, Method2);

        // Using an anonymous method.
        Parallel.For(0, N, delegate(int i)
        {
            // Do Work.
        });

    //Using ForEach
    Parallel.ForEach(collection,item=>DoWork(item));

        // Using a lambda expression.
        Parallel.For(0, N, i =>
        {
            // Do Work.
        });
    }

    static void Method2(int i)
    {
        // Do work.
    }
}

個人認爲在這功能有點語法糖感覺。原因有兩個:
原因一:從這個語法的本身而言,其使用多線程處理操作,本身就會消耗資源,因此parallel類更適合處理較複雜、耗時較長的操作。範圍縮小了!
原因二:即使處理較複雜、耗時較長的任務,在業務上也大多是使用同一數據庫事務,這樣就能保證要麼全成功要麼全失敗。而使用parallel類出現部分失敗時,對於業務而言就比較困難了。
綜上,個人認爲對於parallel適應的範圍不是很大。
定時器
定時器需要好好的寫寫!我的業務程序有一需求,就是比較頻繁的定時查找數據,並將數據打印出來!之前使用WinForm界面上的timer,結果悲劇,當處理大量數據時,會存在兩個問題:1、界面卡死;2、相同的內容會打印多遍(一般會打印2遍)。這兩個問題那段時間經常被業務部門投訴!後來使用了多線程的定時器,解決了這個問題。
System.Threading.Timer類的構造函數如下所示:

public Timer(TimerCallback callback,object state,int dueTime,int period)

Timer構造器中四個參數的的定義分別如下:
callback的委託定義如下

public delegate void TimerCallback(
    object state
)

state爲callback的參數值,若爲空,可爲null;
dueTime爲從準備到執行的時間
period爲時間每次操作的時間間隔
因此timer的一般使用方式如下:

//以下代碼爲僞碼

private system.threading.timer doSomeThingTimer=null;

//開始執行定時操作的button按鈕
public void button_click(e)
{
    doSomeThingTimer = new system.threading.timer(doSomeWork,null,5000,timeout.infinite);
}

//具體的業務邏輯方法
private void doSomeWork(object status)
{
    //業務邏輯代碼

    //這兒一定要使用change方法,改變定時操作
    doSomeThingTimer.change(8000,timeout.infinite);
}

以上就是使用timer的具體方法,其中doSomeWork方法中使用了change方法。原因如下:若不使用change方法,而是在timer中定義好操作每次的執行間隔,則會出現以下情況。若操作的時間很長,超過了每次的執行間隔,則線程池就會調用額外的線程去執行操作,相當於有兩個線程分別去執行doSomeWork。因此,爲了避免這種情況的發生,需要在操作中使用change方法。
Timer小結
1、system.threading.timer類是使用線程池線程,其內部使用了Threadpool.queueUserWorkItem()方法。這也是爲何timer的委託與queueUserWorkItem的委託一致的原因。
2、在線程池內部,線程池爲所有的system.threading.timer共同使用一個線程。若一個不夠用,則會額外再開立新的線程
3、system.windows.forms.timer中也有一個定時器,但該定時器屬於UI線程,實UI線程有一消息泵,定時啓動該定時器。這個定時器適合用於非常簡單、耗時短、更新界面相關的操作。用於後臺的或耗時的操作,請不要使用
4、system.windows.threading.dispatcherTimer類是system.windows.forms.timer在wpf和silverlight的等價物
5、system.timers.timer類。這個類很有意思,它是一個控件,當定時觸發時,它會調用CLR的線程池線程進行操作。按理說它是正合適,但是它也有歷史,它是不應該存在的。因爲在微軟大規模整理線程和定時器相關的方法之前,就把它留在了FCL中,因此由於歷史原因,它也就沒被刪除。但平時儘量不要使用它。
線程池相關
當前線程池的上限是1000,一般情況下不要更改線程池的任何限定。
線程池原理
當使用threadpool.queueuserworkitem()及system.threding.timer創建的工作項會存放到上圖中的全局隊列中。然後線程池中的線程(也就是圖中的工作者線程),會按照FIFO(先入先出,隊列)的原則從全局隊列中獲取工作項,以進行完成。在這個過程中全局隊列有一個同步鎖,防止多個線程搶奪一個工作項。當工作者線程選取工作項後,全局隊列就會將該工作項在列表中刪除。
若此時,創建了一個task,則線程池會將task工作項放置在上圖中的本地隊列中。從而工作者線程就會優先到本地隊列中獲取工作項。選取方式與全局隊列有區別,是後入先出的原則(即棧)獲取工作項。若本地隊列中已經沒了工作項,則線程會到其他本地隊列中“偷取”工作項進行處理。若所有的本地隊列中的工作項都處理完了,則工作者線程就到全局隊列中獲取工作項。若全局隊列中的工作項都處理完了,則工作者線程就會睡眠,然後一定時間段後自然醒來,若發現還沒有事情做,就自殺掉。
若是一個外部的非工作者線程(例如,window線程),則不管是Threadpool還是timer還是task,CLR都將其放入全局隊列中,非工作者線程會從全局隊列中獲取工作項以進行工作。
僞共享
我的系統是64位的操作系統,這也就意味着,系統可以一次性讀取64byte的數據,因此創建2個int32的數據時,很有肯能兩個int32變量存儲在一個緩存線裏。因此有兩個線程對兩個變量進行多次操作的時候,就會因讀取相同的內容而進行資源的爭奪,因此會造成性能的降低。因此爲了避免這種“僞共享”的情況導致的性能下降,可以採用一些attribute,使兩個字段分配到兩個緩存線中,這樣就會使性能提升。
26章小結
主要就是講述了task。
待解決問題
整理attribute
26章完

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章