您好,登錄后才能下訂單哦!
活躍性
活躍性是指好的事情最終會發生。例如,如果你代碼的目標是確保你能夠持續從數組中push和pop對象,問題是這個過程是否能夠永遠工作。使用鎖帶來的問題是鎖會引起系統中所有線程一直等待 -- 換句話說,就是死鎖。如果你能保證你應用的活躍性,那么死鎖應該永遠都不會發生。
問題
想象你有兩個線程:A和B。在Astart之前,A一直等待B結束。但是,在線程B繼續運行之前,B一直等待線程A結束。
對于一個實際的例子,你可以在Listing 6-7中查看它的代碼。注意push和pop線程被同一對象鎖住,即lockedObj。圖6-7顯示了這兩個線程是如何在同一個對象上鎖住,并永遠的互相等待。
在新的例子中你可以看到,pop線程一直在while循環上等待,因為storages數組的大小為0。同時push線程不能往數組中添加對象,因為代碼被對象lockedObj鎖住了。push線程現在必須等待pop線程執行完,然后把鎖歸還給lockedObj對象。因此,這兩個線程停止,然后永遠的等待對方。這里有一些死鎖問題的解決辦法。
如果你的代碼發生了死鎖,你不能再使用@synchronized(lockedObj),因為這樣直接使用是不能避免死鎖的。
NSLock:你可以使用它來保護并發訪問的代碼塊,就像使用@synchronized(obj)那樣,但是當這部分代碼lock和unlock時,你能夠控制它。
NSCondition:這對于生產者和消費者模式是非常有用的,就像前面顯示的push和pop這個例子。
NSLock 解決辦法
你可以有兩種使用方式,lock或tryLock。使用lock的方法,這個方法不能在獲取鎖,它會停止,然后等待直到它獲取到鎖。使用tryLock,如果方法返回NO,意味著鎖已經被其他線程占有,調用的線程不能獲取它。
它已經測試了,如果獲取到鎖就繼續執行。如果你沒有獲取到鎖,一切正常,線程會繼續執行其他沒有使用鎖的代碼。
NSCondition 解決辦法
使用NSLock,你會看到通過使用[testLock lock]來獲取一個鎖;你不能停止或掛起你的線程來等待一些條件。你唯一能做的就是繼續執行直到你釋放鎖,這樣其他線程才能獲取鎖執行。
再看看push,pop這個例子。讓線程不停的運行檢查數組是否有數據,效率是不高的。在循環中,如果線程發現數組中沒有數據,它應該停止然后等待數組有數據了,就把它給取出來。這種方法的好處是你能夠掛起你的線程,而不會浪費系統的資源。
為了讓一個線程停止,等待,還有同時返回一個鎖,你需要使用NSCondition。Listing 6-8 演示了如何使用NSCondition來執行push,pop這個例子。
還有其他的鎖機制,如NSRecursiveLock和NSConditionLock,但是使用NSLock和NSCondition在大部分情況下就足夠了。對于線程,你應該總是簡單明了,因為多線程會在你的代碼中引入不確定的bug。
注意:NSRecursiveLock是非常有用的,如果你有一個線程想要多次獲取一個鎖,而又不會發生死鎖的話。NSRecursiveLock依然會阻塞其他線程來訪問代碼塊。 |
死鎖
使用鎖可能會導致死鎖的發生。死鎖就像前面介紹的那樣,但是更多的情況是,有兩個或多個鎖時,然后線程之間相互等待。
圖6-8演示了線程1獲取object1對象鎖和線程2獲取object2對象鎖的解決辦法。然后線程1想要獲取object2的鎖,但是必須等待線程2釋放這個鎖。同時,線程2想要獲取object1的鎖,但是必須等待線程1釋放這個鎖。正如你看到的,兩個線程互相等待,沒有一個能繼續運行。
有一些方法能夠解決死鎖問題,比如reordering threads;minimize locking;a bigger lock;tryLock;time out for locking。這些機制都不難實現;下面的這些圖能夠幫助你理解他們是如何工作的。
在圖6-9中,最簡單的方式就是對線程重排序,然后順序鎖定,這樣只有當一個使用lock2的線程結束時,其他線程才能獲取鎖,然后繼續執行。
但是,如果你的方法結構是固定的,有一些代碼是屬于第三方庫,很難重排序鎖或修改代碼。
接下里的一種方法是只有你確實需要鎖住的那部分才嘗試鎖住。這中方法使的鎖住的部分最小,如圖6-10.
如果有代碼或想把你不需要鎖住的代碼單獨分離出來,這種方式是很容易實現的。注意它會使你的代碼變得更復雜。
另外一種方法是,在其他鎖上實現另外一個更大的鎖。這允許其他線程嘗試同時訪問同樣的鎖。例如,在圖6-11中,由于一個新的更大的鎖,線程2現在必須等待線程1完成,在它能獲取其他鎖或運行必要的代碼之前。在正常的代碼中,如果能夠從更小的鎖中移除,用更大的鎖取代,你應該這樣做,當它能夠減少代碼的復雜性時。這種機制通常使用在,當你需要在庫(你不能或不想修改庫中的代碼)中防止死鎖時。
你應該嘗試使用tryLock。使用tryLock,如果一個線程不能獲取到鎖,它不會停止和等待。線程能夠繼續執行線程中的其他代碼。這是一個使用tryLock的例子:
我介紹的最后一種方法是使用超時,盡管還有很多其他的方法來防止死鎖。在objective-c中,你可以指定線程來等待,直到你能獲取到鎖或需要等待結束的時間。使用lockBeforeDate方法能幫助你達到這個目的。調用這個方法的線程將會阻塞直到你能獲取到鎖。如果參數中指定的NSDate實例發生了,但是線程還沒有獲取到鎖,它會繼續執行。這在死鎖時會有幫助;你的線程能夠繼續執行,這可能會有一些小的風險,但是不可能發生死鎖。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。