1. 程式人生 > >如何退出當前執行緒及執行緒安全

如何退出當前執行緒及執行緒安全

退出當前執行緒的程式碼

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

// 建立執行緒

    NSThread *thread =[[NSThread alloc]initWithTarget:self selector:@selector(threadDemo2) object:nil];

//開啟執行緒

    [thread start];

}

-(void)threadDemo2{

NSLog(@"%@",[NSThreadcurrentThread]);

    for

(int i = 0; i < 20; ++i) {

        NSLog(@"i:%d",i);

        if (i == 10) {

            /*

             This method uses the currentThread class method to access the current thread. Before exiting the thread, this method posts the NSThreadWillExitNotification with the thread being exited to the default notification center. Because notifications are delivered synchronously, all observers of NSThreadWillExitNotification are guaranteed to receive the notification before the thread exits.

             這個方法使用 currentThread訪問當前執行緒。在推出當前執行緒之前,這個方法發出一個NSThreadWillExitNotification通知給到通知中心。因為通知唄同步地傳送,所有的NSThreadWillExitNotification的觀察者被確保收到這個通知,在退出當前執行緒之前。

             Invoking this method should be avoided as it does not give your thread a chance to clean up any resources it allocated during its execution.

喚醒這個方法應該避免,因為他不會給你的執行緒一個清除執行過程中分配的資源的機會。

             */

            [NSThread exit];// 退出當前的執行緒

            NSLog(@"come here");

        }

    }

}

/*

     The stack size of the receiver, in bytes.

     //棧區的大小以位元組為單位

     This value must be in bytes and a multiple of 4KB.

這個值必須是以位元組為單位,可以是4kb的倍數

     To change the stack size, you must set this property before starting your thread. Setting the stack size after the thread has started changes the attribute size (which is reflected by the stackSize method), but it does not affect the actual number of pages set aside for the thread.

為了改變展區的大小,你必須設定它的優先順序在開啟執行緒之前。設定棧區的大小線上程已經啟動之後,改變這個屬性(它被設定呼叫stackSize方法),但是他不禪城實際的分配空間的效果。

     */

-------------------------------

多條執行緒同時訪問一塊資源

- (void)viewDidLoad {

    [superviewDidLoad];

self.threadA =[[NSThreadalloc]initWithTarget:selfselector:@selector(saleTicket) object:nil];

    self.threadA.name = @"執行緒A";

self.threadB =[[NSThreadalloc]initWithTarget:selfselector:@selector(saleTicket) object:nil];

    self.threadB.name = @"執行緒B";

self.threadC =[[NSThreadalloc]initWithTarget:selfselector:@selector(saleTicket) object:nil];

    self.threadC.name = @"執行緒C";

}


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self.threadA start];

    [self.threadB start];

    [self.threadC start];

}

-(void)saleTicket{

self.leftTicketCount = 100;

    while (YES) {

        if (self.leftTicketCount > 0) {

            self.leftTicketCount --;

            NSLog(@"%@賣票,當前還剩下%ld張票",[NSThread currentThread].name,self.leftTicketCount);

        }else{

            NSLog(@"已賣完");

            return;

        }

    }

}

--執行結果

執行緒C賣票,當前還剩下99張票

2016-05-14 21:00:31.377 03- 執行緒安全[2725:348472] 執行緒B賣票,當前還剩下99張票

2016-05-14 21:00:31.377 03- 執行緒安全[2725:348471] 執行緒A賣票,當前還剩下99張票

2016-05-14 21:00:31.377 03- 執行緒安全[2725:348473] 執行緒C賣票,當前還剩下98張票

執行緒B賣票,當前還剩下82張票

2016-05-14 21:00:31.380 03- 執行緒安全[2725:348471] 執行緒A賣票,當前還剩下81張票

2016-05-14 21:00:31.427 03- 執行緒安全[2725:348472] 執行緒B賣票,當前還剩下79張票

2016-05-14 21:00:31.427 03- 執行緒安全[2725:348473] 執行緒C賣票,當前還剩下80張票

2016-05-14 21:00:31.427 03- 執行緒安全[2725:348471] 執行緒A賣票,當前還剩下78張票

可以看到同時訪問同一塊資源的時候就會出現資料安全問題

-->如果在賣票之前執行緒睡眠一段時間,資料混亂更加明顯

--->加同步鎖 ,當前執行緒訪問的時候加鎖,讀取資料,寫入資料後,再開啟同步鎖,其他物件可以就訪問這個資源。

--->我先把同步鎖載入外面---載入外面的結果是隻能保證一個執行緒能夠賣完

-(void)saleTicket{

self.leftTicketCount = 100;

@synchronized(self) {

        while (YES) {

            if (self.leftTicketCount > 0) {

//[NSThread sleepForTimeInterval:1.0];

                self.leftTicketCount --;

                NSLog(@"%@賣票,當前還剩下%ld張票",[NSThread currentThread].name,self.leftTicketCount);

            }else{

                NSLog(@"已賣完");

                return;

            }

        }

    }

}

輸出結果:

7891] 執行緒A賣票,當前還剩下7張票

2016-05-14 21:09:27.961 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下6張票

2016-05-14 21:09:27.961 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下5張票

2016-05-14 21:09:27.961 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下4張票

2016-05-14 21:09:27.961 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下3張票

2016-05-14 21:09:27.961 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下2張票

2016-05-14 21:09:27.961 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下1張票

2016-05-14 21:09:27.962 03- 執行緒安全[2780:357891] 執行緒A賣票,當前還剩下0張票

鎖在賣票的程式碼裡面呢 ,也出錯 會出現負數

-(void)saleTicket{

self.leftTicketCount = 100;

        while (YES) {

            if (self.leftTicketCount > 0) {

                @synchronized(self) {

//[NSThread sleepForTimeInterval:1.0];

                self.leftTicketCount --;

                NSLog(@"%@賣票,當前還剩下%ld張票",[NSThread currentThread].name,self.leftTicketCount);

                }

            }else{

                return;

            }

        }

//    }

}

輸出結果:

執行緒A賣票,當前還剩下1張票

2016-05-14 21:11:46.893 03- 執行緒安全[2802:360854] 執行緒B賣票,當前還剩下0張票

2016-05-14 21:11:46.894 03- 執行緒安全[2802:360855] 執行緒C賣票,當前還剩下-1張票

2016-05-14 21:11:46.894 03- 執行緒安全[2802:360853] 執行緒A賣票,當前還剩下-2張票

這樣加鎖才是正確的做法

-(void)saleTicket{

self.leftTicketCount = 100;

        while (YES) {

@synchronized(self) {

            if (self.leftTicketCount > 0) {

                self.leftTicketCount --;

                NSLog(@"%@賣票,當前還剩下%ld張票",[NSThread currentThread].name,self.leftTicketCount);

            }else{

                return;

            }

        }

    }

}

其實這個加鎖事利用了執行緒同步,執行緒同步,就是多條執行緒在同一條執行緒上按順序執行。

互斥鎖:上面這個案例就是加一個互斥鎖

原子屬性和非原子屬性 atomic & nonatomic

@property修飾的屬性,不加nonatomic預設就是atomic.

atomic:修飾的屬性預設是執行緒安全的。會給屬性的setter方法加鎖

nonatomic:開發中建議使用這個,因為移動裝置資源少,atomic需要消耗大量的 CPU資源。

關於iOS開發的建議

1.建議所有的屬性都用nonatomic 

2.儘量避免多條執行緒同時訪問同一塊資源。

3.儘量將加鎖,資源搶奪的業務邏輯交給伺服器,減少移動端的壓力