你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS多線程之NSThread

iOS多線程之NSThread

編輯:IOS開發綜合

1.線程簡介

當我們需要處理一個非常繁重的任務的時候,為了避免阻塞主線程的執行(主線程主要負責用戶交互和相關事件處理),我們需要使用線程。當我們使用線程來把很大的任務劃分成一些小的任務在多核機器上並發的執行的時候,可以大大提高我們程序的性能。NSTread提供給我們了執行線程的管理。

2.線程開銷

線程是需要內存和性能開銷的,內存開銷包括系統內核內存和應用程序內存。用來管理和協調線程的內核結構存儲在內核。線程的棧空間和每個線程的數據存儲在程序的內存空間。占用內存的這些結構大部分是在線程創建的時候生成和初始化的。因為要和內核交互,所以這個過程是非常耗時的。線程創建大概的開銷如下(其中第二線程的棧空間是可以配置的):

  • 內核數據結構:大約1KB
  • 占空間:主線程大約1MB,第二線程大約512KB
  • 線程創建時間:大約90毫秒

    另外一個開銷就是程序內線程同步的開銷。

    3.創建線程

    使用NSThread創建線程一共有4中方法:

    • 使用NSThread類方法+detachNewThreadSelector:toTarget:withObject:創建線程並執行
      [NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

      • 使用NSObject的線程擴展(NSThreadPerformAdditions)方法+performSelectorInBackground:withObject:創建並執行線程
        [object performSelectorInBackground:@selector(myThreadMainMethod:) withObject:nil];

        • 創建一個NSThread對象,然後調用start方法執行
          NSThread* aThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];
          [aThread start];

          • 創建一個NSThread子類,然後實例化調用start方法
            #import 
            @interface Thread : NSThread
            @end
            #import "Thread.h"@implementation Thread
            - (void)main
            {
                @autoreleasepool
                {
                    //do thread task
                }
            }
            @end//調用
            Thread *thread = [[Thread alloc] init];
            [thread start];

            4.配置線程參數

            4.1配置線程棧空間

            棧空間是用來存儲為線程創建的本地變量的,棧空間的大小必須在線程的創建之前設定。不能使用創建線程的第一和第二種方法。在調用NSThread的start方法之前通過setStackSize: 設定新的棧空間大小。

            4.2配置線程的本地存儲

            每個線程都維護一個在線程任何地方都能獲取的字典。 我們可以使用NSThread的threadDictionary方法獲取一個NSMutableDictionary對象,然後添加我們需要的字段和數據。

            4.3設置線程的Detached狀態

            通過NSThread創建的線程都是Detached的。如果想要創建joinable線程,只能通過POSIX線程接口創建。

            4.4設置線程的優先級

            每一個新的線程都有一個默認的優先級。系統的內核調度算法根據線程的優先級來決定線程的執行順序。通常情況下我們不要改變線程的優先級,提高一些線程的優先級可能會導致低優先級的線程一直得不到執行,如果在我們的應用內存在高優先級線程和低優先級線程的交互的話,因為低優先級的線程得不到執行可能阻塞其他線程的執行。這樣會對應用造成性能瓶頸。可以通過NSThread的setThreadPriority:方法設置線程優先級,優先級為0.0到1.0的double類型,1.0為最高優先級。

            5.完成線程入口

            5.1創建一個Autorelease Pool

            在線程的入口處我們需要創建一個Autorelease Pool,當線程退出的時候釋放這個Autorelease Pool。這樣在線程中創建的autorelease對象就可以在線程結束的時候釋放,避免過多的延遲釋放造成程序占用過多的內存。如果是一個長壽命的線程的話,應該創建更多的Autorelease Pool來達到這個目的。例如線程中用到了run loop的時候,每一次的迭代都需要創建Autorelease Pool。在非ARC的情況下創建Autorelease Pool代碼如下:

            - (void)myThreadMainRoutine
            {
                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
                // Do thread work here.
                [pool release];  // Release the objects in the pool.
            }

            在ARC環境的代碼如下:

            - (void)myThreadMainRoutine
            {
                @autoreleasepool
                {
                    //do thread task
                }
            }

            5.2設置異常處理

            如果我們的應用能夠捕獲並處理異常,那我們自己創建的線程就應該捕獲異常,如果不這樣的話,當線程出問題的時候,應用就會Crash。

            5.3設置Run Loop

            當創建線程的時候我們有兩種選擇,一種是線程執行一個很長的任務然後再任務結束的時候退出。另外一種是線程可以進入一個循環,然後處理動態到達的任務。每一個線程默認都有一個NSRunloop,主線程是默認開啟的,其他線程要手動開啟。關於NSRunloop的理解和使用下一章討論。

            6.終止線程

            終止線程最好不要用POSIX接口直接殺死線程,這種粗暴的方法會導致系統無法回收線程使用的資源,造成內存洩露,還有可能對程序的運行造成影響。終止線程最好的方式是能夠讓線程接收取消和退出消息,這樣線程在受到消息的時候就有機會清理已持有的資源,避免內存洩露。這種方案的一種實現方式是使用NSRunloop的input source來接收消息,每一次的NSRunloop循環都檢查退出條件是否為YES,如果為YES退出循環回收資源,如果為NO,則進入下一次NSRunloop循環。例子代碼如下:

            - (void)threadMainRoutine
            {
                BOOL moreWorkToDo = YES;
                BOOL exitNow = NO;
                NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
            
                // Add the exitNow BOOL to the thread dictionary.
                NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
                [threadDict setValue:[NSNumber numberWithBool:exitNow] forKey:@"ThreadShouldExitNow"];
            
                // Install an input source.
                [self myInstallCustomInputSource];
            
                while (moreWorkToDo && !exitNow)
                {
                    // Do one chunk of a larger body of work here.
                    // Change the value of the moreWorkToDo Boolean when done.
            
                    // Run the run loop but timeout immediately if the input source is not waiting to fire.
                    [runLoop runUntilDate:[NSDate date]];
            
                    // Check to see if an input source handler changed the exitNow value.
                    exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"] boolValue];
                }
            }
            參考鏈接: 1.Threading Programming Guide 2.Concurrency Programming Guide


  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved