數學之路-vb.net並行計算(3),-vb.net並行計算

來源:互聯網
上載者:User

數學之路-vb.net並行計算(3),-vb.net並行計算

接上節,我們可以使用下面語句建立一個執行緒區域變數,利用靜態TLS功能

Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 1)

betterCounter的值初始化為1。在本程式中,jg被初始化為50,並定義成執行緒區域變數

 

        Dim jg As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 50)

 

然後,我們使用jg.Value 來讀寫這個本地變數的值

 jg.Value -= mynum

 


三、動態TLS

Imports System
Imports System.Threading




Module Module1


    Sub Main()


        Dim mythread1 As Thread
        Dim mythread2 As Thread
        Dim mythread3 As Thread
        '建立線程對象
        mythread1 = New Thread(AddressOf mythreadrun)
        mythread2 = New Thread(AddressOf mythreadrun)
        mythread3 = New Thread(AddressOf mythreadrun)
        Console.WriteLine(Now.ToLongTimeString & "線程對象建立完畢,開始執行線程")


        '執行線程
        mythread1.Start("線程1")
        mythread2.Start("線程2")
        mythread3.Start("線程3")
        '等待線程完成
        mythread1.Join()
        mythread2.Join()
        mythread3.Join()
        '線程執行完畢
        Console.WriteLine(Now.ToLongTimeString & "線程執行完畢!")
    End Sub
    Public Sub mythreadrun(ByVal data As Object)
        Dim mynum As Integer
        '分配一個新的槽,這個槽存放執行緒區域資料,槽名稱
        '必須唯一
        Thread.AllocateNamedDataSlot(data)
        Dim jg As LocalDataStoreSlot
        jg = Thread.GetNamedDataSlot(data)
        Thread.SetData(jg, 100)
        Try
            For mynum = 1 To 10
                Thread.SetData(jg, Thread.GetData(jg) - mynum)
                Console.WriteLine(data & "  " & Now.ToLongTimeString & "=>" & (Thread.GetData(jg) + mynum) & "-" & mynum & ",計算結果為:" & Thread.GetData(jg))
                Thread.Sleep(2)
            Next
        Catch
            Console.WriteLine(data & "  " & Now.ToLongTimeString & "線程異常終止!")
            '終止線程
            Thread.CurrentThread.Abort()
        Finally
            Thread.FreeNamedDataSlot(data)
        End Try
    End Sub
End Module

運行結果如我們所願,jg變數通過動態TLS提供的槽機制實現了執行緒區域變數

 

 

 

        '分配一個新的槽,這個槽存放執行緒區域資料,槽名稱為
        'myjg,名稱必須唯一
        Thread.AllocateNamedDataSlot(data)
        Dim jg As LocalDataStoreSlot
        jg = Thread.GetNamedDataSlot(data)
        Thread.SetData(jg, 100)
以上代碼是關健,我們使用命名資料槽,當然,我們也可以使用未命名槽,因為未命名資料槽相對較簡單,所以這裡使用了命名資料槽,向大家示範一下其功能。

注意:

如果使用 AllocateNamedDataSlot 方法已指派已經存在的指定名稱的槽,此方法會引發異常,且無法測試是否已指派某個槽。另外,使用此方法分配的資料槽必須使用 FreeNamedDataSlot 來釋放。

 

 

本部落格所有內容是原創,如果轉載請註明來源http://blog.csdn.net/myhaspl/

本例中,我們分配槽使用下面語句

Thread.AllocateNamedDataSlot(data)

擷取某個命名槽的引用,以便進行下一步操作

jg = Thread.GetNamedDataSlot(data)

 

Thread.SetData和Thread.GetData可寫、讀槽中資料

四、資料槽的值線上程或內容物件之間不共用

LocalDataStoreSlot 結構可用作本機存放區記憶體機制,線程和上下文可以使用此機制分別儲存線程特定的資料和上下文特定的資料。 通用語言執行平台在建立每個進程時給它分配一個多槽資料存放區區數組。 線程或上下文調用各種函數在資料存放區區中分配資料槽、在槽記憶體儲和檢索資料值、以及釋放資料槽以便線上程或上下文到期後重新使用它。

對於每個線程或上下文,資料槽都是唯一的;它們的值線上程或內容物件之間不共用。 資料槽可根據名稱或根據索引號來分配。

我們可以從下面程式看出


Imports System
Imports System.Threading




Module Module1


    Sub Main()


        Dim mythread1 As Thread
        Dim mythread2 As Thread
        Dim mythread3 As Thread
        Dim jg As LocalDataStoreSlot
        '建立線程對象
        mythread1 = New Thread(AddressOf mythreadrun)
        mythread2 = New Thread(AddressOf mythreadrun)
        mythread3 = New Thread(AddressOf mythreadrun)
        Console.WriteLine(Now.ToLongTimeString & "線程對象建立完畢,開始執行線程")
        'jg = Thread.AllocateNamedDataSlot("myjg")
        ' Thread.SetData(jg, 100)
        '執行線程
        mythread1.Start("線程1")
        mythread2.Start("線程2")
        mythread3.Start("線程3")
        '等待線程完成
        mythread1.Join()
        mythread2.Join()
        mythread3.Join()
        '線程執行完畢
        Console.WriteLine(Now.ToLongTimeString & "線程執行完畢!")
        Thread.FreeNamedDataSlot("myjg")
    End Sub
    Public Sub mythreadrun(ByVal data As Object)
        Dim randomGenerator As New Random()
        Dim mynum As Integer
        '分配一個新的槽,這個槽存放執行緒區域資料,槽名稱為   
        'myjg   
        Dim jg As LocalDataStoreSlot
        Try
            jg = Thread.AllocateNamedDataSlot("myjg")
        Catch
            jg = Thread.GetNamedDataSlot("myjg")
        End Try
        Thread.SetData(jg, 100)
        Try
            For mynum = 1 To 10
                Thread.SetData(jg, Thread.GetData(jg) - mynum)
                Console.WriteLine(data & "  " & Now.ToLongTimeString & "=>" & (Thread.GetData(jg) + mynum) & "-" & mynum & ",計算結果為:" & Thread.GetData(jg))
                Thread.Sleep(randomGenerator.Next(10, 200))
            Next
        Catch
            Console.WriteLine(data & "  " & Now.ToLongTimeString & "線程異常終止!")
            '終止線程
            Thread.CurrentThread.Abort()
        End Try
    End Sub
End Module

本部落格所有內容是原創,如果轉載請註明來源http://blog.csdn.net/myhaspl/

為了查看效果,我特意用隨機數來代替固定的sleep時間,這樣更有說明力。上面程式的運行結果如下:

 

 

 

五、TLS小結

1)TLS基礎

可以使用託管執行緒區域儲存區 (TLS) 儲存某一線程和應用程式定義域所專屬的資料。 .NET Framework 提供了兩種使用託管 TLS 的方式:線程相關的靜態欄位和資料槽。 線程相關的靜態欄位提供的效能比資料槽的效能要好得多,而且它還啟用了編譯時間類型檢查。


如果您可以在編譯時間預料到您的確切需要,請使用線程相關的靜態欄位(在 Visual Basic 中為線程相關的 Shared 欄位)。 線程相關的靜態欄位可提供最佳效能。 它們還具備編譯時間類型檢查的優點。 

如果只能在運行時發現您的實際需要,請使用資料槽。 資料槽比線程相關的靜態欄位慢一些且更加難於使用,並且資料存放區為 Object 類型,因此必須將其強制轉換為正確的類型才能使用。 

 2)2種TLS特點a)無論是使用線程相關的靜態欄位還是使用資料槽,託管 TLS 中的資料都是線程和應用程式定義域組合所專屬的。 
在應用程式定義域內部,一個線程不能修改另一個線程中的資料,即使這兩個線程使用同一個欄位或槽時也不能。當線程從多個應用程式定義域中訪問同一個欄位或槽時,會在每個應用程式定義域中維護一個單獨的值。例如,如果某個線程設定線程相關的靜態欄位的值,接著它進入另一個應用程式定義域,然後檢索該欄位的值,則在第二個應用程式定義域中檢索的值將不同於第一個應用程式定義域中的值。 在第二個應用程式定義域中為該欄位設定一個新值不會影響第一個應用程式定義域中該欄位的值。 同樣,當某個線程擷取兩個不同應用程式定義域中的同一命名資料槽時,第一個應用程式定義域中的資料將始終與第二個應用程式定義域中的資料無關。b)如果您知道一些資料總是某個線程和應用程式定義域組合所專屬的,請向該靜態欄位應用 ThreadStaticAttribute 特性。 與使用任何其他靜態欄位一樣使用該欄位。 該欄位中的資料是每個使用它的線程所專屬的。線程相關的靜態欄位的效能優於資料槽,並且具有編譯時間類型檢查的優點。c)請注意,任何類建構函式代碼都將在訪問該欄位的第一個上下文中的第一個線程上運行。 在同一應用程式定義域內的所有其他線程或上下文中,如果欄位是參考型別,它們將被初始化為 null(在 Visual Basic 中為 Nothing);如果欄位是實值型別,它們將被初始化為它們的預設值。 因此,您不應依賴於類建構函式來初始化線程相關的靜態欄位。 而應避免初始化線程相關的靜態欄位並假定它們初始化為 null (Nothing) 或它們的預設值。d)在 .NET Framework 4 版中,可以使用 System.Threading.ThreadLocal(Of T) 類建立執行緒區域對象,在第一次使用該對象時它將惰式初始化,這樣就解決了c中所指問題。d).NET Framework 提供了線程和應用程式定義域組合所專屬的動態資料槽。 資料槽包括兩種類型:命名槽和未命名槽。 兩者都是通過使用 LocalDataStoreSlot 結構來實現的。若要建立命名資料槽,請使用 Thread.AllocateNamedDataSlot 或 Thread.GetNamedDataSlot 方法。 若要擷取對某個現有命名槽的引用,請將其名稱傳遞給 GetNamedDataSlot 方法。若要建立未命名資料槽,請使用 Thread.AllocateDataSlot 方法。e)對於命名槽和未命名槽,請使用 Thread.SetData 和 Thread.GetData 方法設定和檢索槽中的資訊。 這些都是靜態方法,它們始終作用於當前正在執行它們的線程的資料。f)命名槽可能很方便,因為您可以在需要它時通過將其名稱傳遞給 GetNamedDataSlot 方法來檢索該槽,而不是維護對未命名槽的引用。 但是,如果另一個組件使用相同的名稱來命名其線程相關的儲存區,並且有一個線程同時執行來自您的組件和該組件的代碼,則這兩個組件可能會破壞彼此的資料。 (本方案假定這兩個組件在同一應用程式定義域內運行,並且它們並不用於共用相同資料。)g)線程使用本機存放區記憶體機制來儲存線程特定的資料。 通用語言執行平台在建立每個進程時給它分配一個多槽資料存放區區數組。 線程可以分配資料存放區區中的資料槽,儲存和檢索槽中的資料值,以及線上程到期之後釋放槽以供重新使用。 每個線程的資料槽都是唯一的。 其他任何線程(即便是子線程)均無法擷取該資料。

1、用調試器調試線程

 1)棧調用

以下面代碼為例

Imports System.Threading


Public Class Form1


    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim main_x As Integer
        main_x = 5
        Call sub1(main_x)
    End Sub
    Private Sub sub1(sub1_x As Integer)
        Dim jg As Integer
        jg = sub1_x * sub1_x
        Call sub2(jg)
    End Sub
    Private Sub sub2(sub2_x As Integer)
        Dim jg As Integer
        jg = sub2_x * 2
        '在下一句設定斷點
        jg = jg * jg


    End Sub


End Class

我們首先來看呼叫堆疊,在調試菜單中選擇呼叫堆疊,可看到過程的調用順序:



 然後查看線程



 最後查看局部變數



 此外,我們還可以研究一下這個線程的調用時堆棧情況,通過反組譯碼代碼,在呼叫堆疊視窗中選擇“轉到反組譯碼”

   Private Sub sub2(sub2_x As Integer)
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  sub         esp,14h 
00000006  mov         dword ptr [ebp-10h],ecx 
00000009  mov         dword ptr [ebp-4],edx 
0000000c  cmp         dword ptr ds:[0256B1B8h],0 
00000013  je          0000001A 
00000015  call        62A16743 
0000001a  xor         edx,edx 
0000001c  mov         dword ptr [ebp-8],edx 
0000001f  mov         eax,dword ptr [ebp-10h] 
00000022  mov         dword ptr [ebp-14h],eax 
00000025  mov         ecx,dword ptr [ebp-10h] 
00000028  call        628F5C25 
0000002d  mov         dword ptr [ebp-0Ch],eax 
00000030  push        32h 
00000032  mov         edx,dword ptr [ebp-0Ch] 
00000035  mov         ecx,dword ptr [ebp-14h] 
00000038  call        FFDF30D0 
0000003d  mov         ecx,dword ptr [ebp-4] 
00000040  call        FFFFFF70 
00000045  mov         ecx,63h 
0000004a  call        FFDF2940 
0000004f  nop 
        Dim jg As Integer
        jg = sub2_x * 2
00000050  mov         eax,dword ptr [ebp-4] 
00000053  mov         edx,2 
00000058  imul        eax,eax,2 
0000005b  jno         00000062 
0000005d  call        62A19A30 
00000062  mov         dword ptr [ebp-8],eax 
        '在下一句設定斷點
        jg = jg * jg
00000065  mov         eax,dword ptr [ebp-8] 
00000068  imul        eax,dword ptr [ebp-8] 
0000006c  jno         00000073 
0000006e  call        62A19A30 
00000073  mov         dword ptr [ebp-8],eax 


    End Sub
00000076  nop 
00000077  nop 
00000078  mov         ecx,63h 
0000007d  call        FFDF2A60 
00000082  nop 
00000083  mov         esp,ebp 
00000085  pop         ebp 
00000086  ret 

ESP是棧頂指標

ebp是基址指標

+-----+

+基址 +

+-----+

+棧內容+

+棧內容+

 

+棧內容+

+棧內容+

+棧內容+

 

+棧頂   +

如所示,基地的地址比棧頂的地址大,就是向下增長。

寄存器ebp和esp儲存著當前的基址和棧頂地址

首先,進入函數時

00000000  push        ebp 
備份基址指標

00000001  mov         ebp,esp

然後設定基址指標指向棧頂,相當於為當前棧清空了內容,做好在棧中分配局部變數的準備
00000003  sub         esp,14h

完成棧(可以理解為本函數可訪問的棧)的空間分配,將棧頂指標向下增長14h(向下增長的意思是棧的空間增長規律是地址遞減)。相當於棧中已經容納了14h的空間

 

        Dim jg As Integer
        jg = sub2_x * 2
00000050  mov         eax,dword ptr [ebp-4] 
00000053  mov         edx,2 
00000058  imul        eax,eax,2 
0000005b  jno         00000062 
0000005d  call        617391D0 
00000062  mov         dword ptr [ebp-8],eax 
        '在下一句設定斷點
        jg = jg * jg
00000065  mov         eax,dword ptr [ebp-8] 
00000068  imul        eax,dword ptr [ebp-8] 
0000006c  jno         00000073 
0000006e  call        617391D0 
00000073  mov         dword ptr [ebp-8],eax

 

從上面這段代碼可以看出來

sub2_x分配在了dword ptr [ebp-4] ,而jg分配在了dword ptr [ebp-8]

 

最後,離開函數時,恢複進入函數前棧的指標,相當於釋放了本次在棧中分配的空間

    End Sub
00000083  mov         esp,ebp 
恢複棧頂指標

00000085  pop         ebp

恢複基址指標


00000086  ret

 

2、修改預設棧的大小

         Dim 線程變數名 As Thread = New Thread(函數名,以位元組為單位的棧大小)

比如

        Dim mythread As Thread = New Thread(myfun,1024*512)

分配了512kb位元組

 



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.